in c#, programming, tutorial, Unity3D

Asynchronous Serial Communication

Share Button

In a previous post, How To Integrate Arduino With Unity, we have shown how data can be sent and received through the serial port. The main problem encountered was dealing with the latency and the delays that communicating across different devices introduces. The solution proposed in that tutorial used a coroutine; decoupling the device communication from the game logic attenuates the problem, but it doesn’t remove it. The main problem is that, despite the name, Unity coroutines are not really executed in parallel with the rest of the game. Unity, by design, is not thread safe. This means that true parallelism is intentionally avoided by the engine, as it could result in race conditions. To solve the problem of the communication between Unity and Arduino, once and for all, we need actual threads.

Step 1. Creating the Thread

C# supports threads with its System.Threading library. When a new thread is created, a function has to be provided. In the following example, that function is called ThreadLoop .

Step 2. Callbacks and Queues

When code is executed in parallel, the traditional pattern of request and response typical of most imperative languages breaks. We cannot expect a function that, once invoked, returns when some data from Arduino is available. A very common approach relies on callbacks. With callbacks, the logic flow of the game is as follow:

  • A MonoBehavior sends a request to the thread;
  • The thread executes the request in the background;
  • When ready, the thread invoked a callback function that the MonoBehavior has provided.

This was exactly the approach used in How To Integrate Arduino With Unity, implemented in the coroutine AsynchronousReadFromArduino.

That approach, unfortunately, won’t work with threads. The thread that communicates with Arduino can complete its request at any moment in time. As a result, its callback could potentially be executed in an environment that is not thread safe. To make it simple: Unity objects cannot be touched or manipulated in a thread.

The solution revolves around the concept of synchronised queues. When a MonoBehavior script wants to send a request to Arduino, it places it in a input queue. The thread reads it, executes it and when its done it places it into an output queue. The thread never accesses any Unity objects. Those two queues are used to safely send and receive messages across threads.

C# has an effective implementation of synchronised queues that are safe to use between different threads:

Please note that the class Queue, by itself, is not guaranteed to be thread safe. The Queue.Synchronzed wrapper makes an existing queue thread safe (MSDN).

Part 3. Queue Communication

Now that we have two queues, we need some code that allows an external MonoBehavior script to send and receive data. Using the queue in this setup is relatively straightforward:

Please, note that now that we are using queues, it is your script’s responsibility to periodically query the queue for incoming messages.

Part 4. Thread Loop

The final part of code necessary is the actual body of the thread. It consists of two parts; a setup, and a loop. The setup opens the serial connection with Arduino. The loop keeps it open, sending and receiving messages through the synchronised queues:

Note that the functions WriteToArduino and ReadFromArduino are taken from the first part of this tutorial, How To Integrate Arduino With Unity.

Part 5. Improvements

The code shown so far is not very reliable, as it doesn’t take into account possible errors and exceptions. A better implementation should always make sure that all the possible exceptions are captures and properly handled.

Another aspect that has not been considered is the closure of the serial port. There are several approaches that can be done, but they all need a way for a Unity script to alter the state of a thread. For instance, by adding a boolean variable called looping that can be used a guard for the thread loop.

To be pedantic, a script should never directly change the variable used by a thread. Given how simple this script is, it’s safe to assume that there are virtually no side effects. If you want to play safe with thread, each access to a shared variable between different threads should be mediated through a lock, like this:

The keyword lock ensures that no two threads can edit the object at the same time. This is who thread safe code is developed in C#. But it can also be the start of an endless stream of drama called deadlocks.

To ensure thread safety, every potentially concurrent access to the variable looping must be enclosed in a lock block. This also includes the access that is done in the guard of the while loop in the ThreadLoop function:

The method StopThread can now be invoked safely from any Unity script.

Conclusion

This post extends the original tutorial on Arduino and Unity. It shows how a thread can be used to avoid delays when waiting for data on the serial port. This post was inspired by Daniel Wilches’ asset SerialCommUnity.

Support this blog! ♥

In the past two years I've been dedicating more and more of my time to the creation of quality tutorials, mainly about game development and machine learning. If you think these posts have either helped or inspired you, please consider supporting this blog.

PatreonBecome a Patron Oo4th_patreon_name
PaypalDonate on PayPal
Twitter_logoFollow on Twitter

Don't miss the next tutorial!

There's a new post every Wednesday: leave your email to be notified!


Write a Comment

Comment

11 Comments

  1. Although I probably never need to do these stuff, I really enjoy the article.
    It great to see an article that doesn’t just touch the surface of a concept, create a working duct-taped prototype and call it a day, which is basically every Unity tutorial.
    By the way, if you use a leonardo you can just send keyboard signal and don’t have to worry about these serial stuff

    • Hey!
      Yeah, I’ve been using that feature lately! 🙂
      Unfortunately you can’t send any data back to Arduino, which is why I have decided to take a different approach.

    • Hey! I think is a good practice to lock your resources in general. This example was very simple, but if you are going to perform a much complex operation, then you know what you have to do.

  2. Thanks you for your explanation. I have some questions:
    1. I notice that the the serial port is constantly opened and closed inside the ThreadLoop () in your example. If I have only one thread to deal the communication between Unity and Arduino, can I just move the stream.open() to be inside the Start() to execute it once, and close the stream inside the update() loop or OnApplicationQuit() once the game is ended?
    2. Once I have two serial ports, say com1, and com2, to read data from 2 Arduino, Should I have two threads: one to deals with com1 the other one deals with com2? or I can use only one thread with 2 separating receiving and 2 sending queues(inQueue1 inputQueue2, outputQueue1 outputQueue2), and one locking variable (looping) so that I can query com1 first and following by query com2 in each iteration of ThreadLoop ()?

    2.1 If two threads are necessarily for two serial ports, when it is timing to call StopThread()? I was wondering if I only need to call it when the game is finished? (E.g., call it at OnApplicationQuit() ?)

    • Hey!
      (1) The thread is started ONCE. So, the serial port is opened only once (i.e. when the thread starts). You can see that the reading / writing bit is stuck in an endless loop. The opening part is not repeated.

      (2) This all depends on how critical your application is. Theoretically speaking, you can avoid threads all together if you don’t need super fast reaction times. The problem with merging two threads is that is harder to handle errors. Because you now have 4 possible states between the two working/non working threads.

      (2.1) Again, this depends on your application. If there are parts of the game where you are not using the serial port, you can close it to release its resources. There’s no issue opening it again and again, if you’re doing properly and sporadically. :p

  3. Hi Alan, thanks very much for this tutorial. I was just constantly dumping data into the serial buffer before and this approach makes much more sense to me.

    I came across some weird behavior in my Unity implementation and I wondered if you had any advice or insight.

    When using ReadLine(), everything works fine, but I’m reading from the Arduino every frame and I think the amount of data I’m sending through would exceed the limit of the fastest baud rate so instead, I’m sending a buffer of bytes with the arduino Serial.write() command.

    I tested with my Unity app only sending the ping and getting data on a key press and I found that the first two readings would always return junk data. Then after that, each reading would be one reading behind… that is if I change the voltage source from 0v to 5v and take a reading, it will return 0v. If I swing the voltage back to 0v and take another reading, it will return 5v. After another reading without changing the incoming voltage, it will correctly return 0v.

    It’s always accurate when sending via Serial.println() like in your tutorial, but I have this weird lag of one reading when using Serial.write(). Would love to know why because it would seem that I’m always going to be one reading behind this way! Thanks!

  4. Hey Alan, thanks for the tutorial! I learnt a few new things – especially Queue.Synchronized seems useful.

    I was missing one thing though: I think you should add that StopThread() should definitely be called at in OnDestroy(). Otherwise the thread will continue to run in the Unity editor even when you’ve stopped the app, and eventually Unity will crash.