Brain_Arduino/Assets/Ardity/Scripts/SerialController.cs

160 lines
6.6 KiB
C#
Raw Normal View History

2024-12-01 15:24:10 +08:00
/**
* Ardity (Serial Communication for Arduino + Unity)
* Author: Daniel Wilches <dwilches@gmail.com>
*
* This work is released under the Creative Commons Attributions license.
* https://creativecommons.org/licenses/by/2.0/
*/
using UnityEngine;
using System.Threading;
/**
* This class allows a Unity program to continually check for messages from a
* serial device.
*
* It creates a Thread that communicates with the serial port and continually
* polls the messages on the wire.
* That Thread puts all the messages inside a Queue, and this SerialController
* class polls that queue by means of invoking SerialThread.GetSerialMessage().
*
* The serial device must send its messages separated by a newline character.
* Neither the SerialController nor the SerialThread perform any validation
* on the integrity of the message. It's up to the one that makes sense of the
* data.
*/
public class SerialController : MonoBehaviour
{
[Tooltip("Port name with which the SerialPort object will be created.")]
public string portName = "COM3";
[Tooltip("Baud rate that the serial device is using to transmit data.")]
public int baudRate = 9600;
[Tooltip("Reference to an scene object that will receive the events of connection, " +
"disconnection and the messages from the serial device.")]
public GameObject messageListener;
[Tooltip("After an error in the serial communication, or an unsuccessful " +
"connect, how many milliseconds we should wait.")]
public int reconnectionDelay = 1000;
[Tooltip("Maximum number of unread data messages in the queue. " +
"New messages will be discarded.")]
public int maxUnreadMessages = 1;
// Constants used to mark the start and end of a connection. There is no
// way you can generate clashing messages from your serial device, as I
// compare the references of these strings, no their contents. So if you
// send these same strings from the serial device, upon reconstruction they
// will have different reference ids.
public const string SERIAL_DEVICE_CONNECTED = "__Connected__";
public const string SERIAL_DEVICE_DISCONNECTED = "__Disconnected__";
// Internal reference to the Thread and the object that runs in it.
protected Thread thread;
protected SerialThreadLines serialThread;
// ------------------------------------------------------------------------
// Invoked whenever the SerialController gameobject is activated.
// It creates a new thread that tries to connect to the serial device
// and start reading from it.
// ------------------------------------------------------------------------
void OnEnable()
{
serialThread = new SerialThreadLines(portName,
baudRate,
reconnectionDelay,
maxUnreadMessages);
thread = new Thread(new ThreadStart(serialThread.RunForever));
thread.Start();
}
// ------------------------------------------------------------------------
// Invoked whenever the SerialController gameobject is deactivated.
// It stops and destroys the thread that was reading from the serial device.
// ------------------------------------------------------------------------
void OnDisable()
{
// If there is a user-defined tear-down function, execute it before
// closing the underlying COM port.
if (userDefinedTearDownFunction != null)
userDefinedTearDownFunction();
// The serialThread reference should never be null at this point,
// unless an Exception happened in the OnEnable(), in which case I've
// no idea what face Unity will make.
if (serialThread != null)
{
serialThread.RequestStop();
serialThread = null;
}
// This reference shouldn't be null at this point anyway.
if (thread != null)
{
thread.Join();
thread = null;
}
}
// ------------------------------------------------------------------------
// Polls messages from the queue that the SerialThread object keeps. Once a
// message has been polled it is removed from the queue. There are some
// special messages that mark the start/end of the communication with the
// device.
// ------------------------------------------------------------------------
void Update()
{
// If the user prefers to poll the messages instead of receiving them
// via SendMessage, then the message listener should be null.
if (messageListener == null)
return;
// Read the next message from the queue
string message = (string)serialThread.ReadMessage();
if (message == null)
return;
// Check if the message is plain data or a connect/disconnect event.
if (ReferenceEquals(message, SERIAL_DEVICE_CONNECTED))
messageListener.SendMessage("OnConnectionEvent", true);
else if (ReferenceEquals(message, SERIAL_DEVICE_DISCONNECTED))
messageListener.SendMessage("OnConnectionEvent", false);
else
messageListener.SendMessage("OnMessageArrived", message);
}
// ------------------------------------------------------------------------
// Returns a new unread message from the serial device. You only need to
// call this if you don't provide a message listener.
// ------------------------------------------------------------------------
public string ReadSerialMessage()
{
// Read the next message from the queue
return (string)serialThread.ReadMessage();
}
// ------------------------------------------------------------------------
// Puts a message in the outgoing queue. The thread object will send the
// message to the serial device when it considers it's appropriate.
// ------------------------------------------------------------------------
public void SendSerialMessage(string message)
{
serialThread.SendMessage(message);
}
// ------------------------------------------------------------------------
// Executes a user-defined function before Unity closes the COM port, so
// the user can send some tear-down message to the hardware reliably.
// ------------------------------------------------------------------------
public delegate void TearDownFunction();
private TearDownFunction userDefinedTearDownFunction;
public void SetTearDownFunction(TearDownFunction userFunction)
{
this.userDefinedTearDownFunction = userFunction;
}
}