c# simple-code
unity serial communication 유니티 시리얼통신
- 출처 : https://forum.unity.com/threads/serial-communication-between-unity-5-and-arduino.331005/
- Change the API Compatibility Level from “.NET 2.0 Subset” to “.NET 2.0” in Player Settings.
- File > Build Setting > Player Setting > Other Settings > Configuration > Api Compatibility Lavel > .NET 2.0
using System.IO.ports;
public SerialPort serialPort;
public void Start(){
foreach(string str in SerialPort.GetPortNames())
{
Debug.Log(str);
}
}
* System.IO.ports 없음
* Api Compatibility Level : .NET 4.x
using UnityEngine;
using System.Collections;
using System.IO;
using System.IO.Ports;
using System.Threading;
using System.Collections.Generic;
public class serialExample : MonoBehaviour
{
//public static SerialPort serialPort = new SerialPort("/dev/tty.usbmodem1411", 9600, Parity.None, 8, StopBits.One);
public static SerialPort serialPort = new SerialPort("\\\\.\\COM13", 9600, Parity.None, 8, StopBits.One);
public static string strIn;
void Start()
{
GetPortNames();
OpenConnection();
}
void Update()
{
serialPort.Write("1");
}
void GetPortNames()
{
int p = (int)System.Environment.OSVersion.Platform;
List<string> serial_ports = new List<string>();
if(p == 4 || p == 128 || p == 6)
{
string[] ttys = Directory.GetFiles("/dev/", "tty.*");
foreach(string dev in ttys)
{
if (dev.StartsWith("/dev/tty.*"))
serial_ports.Add(dev);
Debug.Log (System.String.Format(dev));
}
}
}
public void OpenConnection()
{
if(serialPort != null)
{
if(serialPort.IsOpen)
{
serialPort.Close();
Debug.Log("Closing port, because it was already open!");
}
else
{
serialPort.Open();
serialPort.ReadTimeout = 50;
Debug.Log("Port Opened!");
}
}
else
{
if(serialPort.IsOpen)
{
print("Port is already open");
}
else
{
print("Port == null");
}
}
}
void OnApplicationQuit()
{
serialPort.Close();
Debug.Log("Port closed!");
}
}
try catch 추가하여 string 데이터 받아오기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Collections;
using System.IO;
using System.IO.Ports;
using System.Threading;
using System.Collections.Generic;
public class RecoilGun : MonoBehaviour {
//public static SerialPort serialPort = new SerialPort("/dev/tty.usbmodem1411", 9600, Parity.None, 8, StopBits.One);
public static SerialPort serialPort = new SerialPort("\\\\.\\COM13", 9600, Parity.None, 8, StopBits.One);
public static string strIn;
void Start()
{
//GetPortNames();
OpenConnection();
}
private string tmp;
void Update()
{
try
{
if (serialPort.IsOpen)
{
tmp = serialPort.ReadLine();
Debug.Log(tmp);
process_data(tmp);
serialPort.ReadTimeout = 30;
}
}
catch (System.TimeoutException e)
{
//Debug.Log(e);
}
//serialPort.Write("1");
}
void process_data(string tmp)
{
}
void GetPortNames()
{
int p = (int)System.Environment.OSVersion.Platform;
List<string> serial_ports = new List<string>();
if (p == 4 || p == 128 || p == 6)
{
string[] ttys = Directory.GetFiles("/dev/", "tty.*");
foreach (string dev in ttys)
{
if (dev.StartsWith("/dev/tty.*"))
serial_ports.Add(dev);
Debug.Log(System.String.Format(dev));
}
}
}
public void OpenConnection()
{
if (serialPort != null)
{
if (serialPort.IsOpen)
{
serialPort.Close();
Debug.Log("Closing port, because it was already open!");
}
else
{
serialPort.Open();
serialPort.ReadTimeout = 50;
Debug.Log("Port Opened!");
}
}
else
{
if (serialPort.IsOpen)
{
print("Port is already open");
}
else
{
print("Port == null");
}
}
}
void OnApplicationQuit()
{
serialPort.Close();
Debug.Log("Port closed!");
}
}
제대로 되는 시리얼 통신
- https://answers.unity.com/questions/400222/arduino-with-unity-bad-framerate.html
- Unity 는 Mono 베이스이기 때문에 리시브 이벤트 핸들러가 동작하지 않는다 -_-;
- https://code-examples.net/ko/q/1c9f1ef
- 위 문제때문에 시리얼통신 누적으로 딜레이가 생겼는데. 얼추 30FPS로 맞춰서 전송해서 해결함
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Collections;
using System.IO;
using System.IO.Ports;
using System.Threading;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine.Events;
using System.Text.RegularExpressions;
public class RecoilGun : MonoBehaviour {
//public static SerialPort serialPort = new SerialPort("/dev/tty.usbmodem1411", 9600, Parity.None, 8, StopBits.One);
public static SerialPort stream = new SerialPort("\\\\.\\COM13", 9600, Parity.None, 8, StopBits.One);
public static string strIn;
public GameObject slider_x;
public GameObject slider_y;
public GameObject joy_bt;
public GameObject bt1;
public GameObject bt2;
public GameObject bt3;
public GameObject bt4;
private Slider slider_x_;
private Slider slider_y_;
private Toggle joy_bt_;
private Toggle bt1_;
private Toggle bt2_;
private Toggle bt3_;
private Toggle bt4_;
void Start()
{
//GetPortNames();
OpenConnection();
slider_x_ = slider_x.GetComponent<Slider>();
slider_y_ = slider_y.GetComponent<Slider>();
joy_bt_ = joy_bt.GetComponent<Toggle>();
bt1_ = bt1.GetComponent<Toggle>();
bt2_ = bt2.GetComponent<Toggle>();
bt3_ = bt3.GetComponent<Toggle>();
bt4_ = bt4.GetComponent<Toggle>();
stream.ErrorReceived += DataErrorReceivedHandler;
/*
try
{
stream.Open();
stream.DataReceived += DataReceivedHandler;
}
catch (System.TimeoutException e)
{
Debug.Log("Could not open serial port: " + e.Message);
}
*/
}
private string tmp;
void Update()
{
try
{
if (stream.IsOpen)
{
string pre_tmp = null;
tmp = null;
/*
do {
tmp = null;
pre_tmp = tmp;
tmp = stream.ReadLine();
Debug.Log(tmp);
} while (tmp != null);
*/
tmp = stream.ReadLine();
//while (stream.ReadByte() != null) { }
//pre_tmp = find_last_one(tmp);
Debug.Log("tmp:"+tmp);
process_data(tmp);
//stream.ReadTimeout = 30;
}
}
catch (System.Exception e)
{
Debug.Log(e);
}
//serialPort.Write("1");
}
private string find_last_one(string raw_data)
{
string result_data = "nope";
Debug.Log("raw_data:"+ raw_data);
string pattern = "^s(.*)e$";
Match mat;
mat = Regex.Match(raw_data, pattern);
Debug.Log("mat.Length:"+mat.Length);
Debug.Log("mat.Value:"+ mat.Value);
result_data = mat.Value;
return mat.Value;
}
public void OpenConnection()
{
if (stream != null)
{
if (stream.IsOpen)
{
stream.Close();
Debug.Log("Closing port, because it was already open!");
}
else
{
stream.DtrEnable = true; // Data-terminal-ready
stream.RtsEnable = true; // Request-to-send
//stream.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
stream.DataReceived += DataReceivedHandler;
stream.Open();
stream.ReadTimeout = 50;
Debug.Log("Port Opened!");
//stream.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
//stream.DataReceived += DataReceivedHandler;
}
}
else
{
if (stream.IsOpen)
{
print("Port is already open");
}
else
{
print("Port == null");
}
}
}
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
Debug.Log("why do not");
SerialPort sp = (SerialPort)sender;
//serialPort.ReadTimeout = 30;
try
{
string b = sp.ReadLine();
Debug.Log(b);
process_data(b);
}
catch (System.TimeoutException ex)
{
Debug.Log(ex);
}
}
private void DataErrorReceivedHandler(object sender,SerialErrorReceivedEventArgs e)
{
Debug.Log("Serial port error:"+e.EventType.ToString("G"));
}
private string[] datas;
void process_data(string tmp)
{
Debug.Log("why do not?");
datas = tmp.Split('|');
//Debug.Log(datas[3]);
// x,y,s bt1,bt2,bt3,bt4
slider_x_.value = int.Parse(datas[0].Substring(1));
slider_y_.value = int.Parse(datas[1]);
joy_bt_.isOn = (datas[2]=="1");
bt1_.isOn = (datas[3] == "1");
bt2_.isOn = (datas[4] == "1");
bt3_.isOn = (datas[5] == "1");
bt4_.isOn = (datas[6] == "1]");
}
public void send_recoil()
{
stream.Write("1");
stream.DiscardOutBuffer();
Debug.Log("send recoil request");
}
void GetPortNames()
{
int p = (int)System.Environment.OSVersion.Platform;
List<string> serial_ports = new List<string>();
if (p == 4 || p == 128 || p == 6)
{
string[] ttys = Directory.GetFiles("/dev/", "tty.*");
foreach (string dev in ttys)
{
if (dev.StartsWith("/dev/tty.*"))
serial_ports.Add(dev);
Debug.Log(System.String.Format(dev));
}
}
}
void OnApplicationQuit()
{
stream.DiscardOutBuffer();
stream.DiscardInBuffer();
stream.Close();
stream.DataReceived -= new SerialDataReceivedEventHandler(DataReceivedHandler);
stream = null;
Debug.Log("Port closed!");
}
}
스레드를 이용한 시리얼통신
- 어쨋든 시리얼이 한번 누적되기 시작하면 딜레이누적으로 문제가 생긴다
- 스레드를 이용하여 데이터를 모두 덜어낼 수 있어야 문제가 없다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO.Ports;
using System.Threading; //쓰레드를 쓰겠다고 선언.
public class SerialPortReporter : MonoBehaviour
{
static private SerialPort my_serial;
public string portName = "\\\\.\\COM35";
private int baudRate = 57600;
private string[] chars = new string[20];
[Range(0, 1)]
public int[] digital_pins = new int[14];
[Range(0, 1)]
public float[] analog_pins = new float[6];
void Start()
{
foreach (string str in SerialPort.GetPortNames()) { Debug.Log(string.Format("Existing COM port: {0}", str)); }
my_serial = new SerialPort(portName, baudRate);
try
{
my_serial.DataReceived += DataReceivedHandler;
my_serial.ReadTimeout = 10;
my_serial.Open();
Debug.Log("open");
}
catch (System.TimeoutException e)
{
Debug.Log("Could not open serial port: " + e.Message);
}
Debug.Log("시작");
ThreadStart th = new ThreadStart(work); //1.work메소드를 위임.
t = new Thread(th); //2.쓰레드생성.
t.Start(); //3.시작
Debug.Log("끝!");
}
private Thread t;
static private string RawData = "";
public static void work() {
while (my_serial.IsOpen)
{
string value = my_serial.ReadLine(); //Read the information
value = value.Trim(); //remove whitespace around our values
RawData = value;
//Debug.Log("ReadLine():" + value);
}
}
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
Debug.Log("is working?");
Debug.Log(e);
}
private string tmp;
private void Update()
{
//Debug.Log("RawData:" + RawData);
chars = RawData.Split('|');
for (int i = 0; i < 14; i++)
{
//Debug.Log("chars[i]:" + chars[i]);
digital_pins[i] = int.Parse(chars[i]);
}
for (int i=0; i<6; i++)
{
analog_pins[i] = float.Parse(chars[14+i])/1023;
}
}
private string[] datas;
void process_data(string tmp)
{
//데이터를 처리합니다.
datas = tmp.Split('|');
//Debug.Log(datas[3]);
// x,y,s bt1,bt2,bt3,bt4
}
void OnApplicationQuit()
{
Debug.Log("Application ending after " + Time.time + " seconds");
t.Abort(); //강제종료
my_serial.Close();
}
}
통신속도차이에 의한 timeout을 해소한 버전
- nano보드는 통신속도가 느려서(그마저도 최신 부트로더에서는 해결되었다던데..) unity쪽에서 timeout을 길게 기다려주지 않으면 통신에러가 난다.
- 아래는 무한정 기다리도록 하여 타임아웃이 나지 않도록 한 버전
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO.Ports;
using System.Threading; //쓰레드를 쓰겠다고 선언.
using System;
public class SerialPortReporter : MonoBehaviour
{
static private SerialPort my_serial;
public string portName = "\\\\.\\COM35";
private int baudRate = 57600;
private string[] chars = new string[20];
[Range(0, 1)]
public int[] digital_pins = new int[14]; //디지털핀 14종
[Range(0, 1)]
public float[] analog_pins = new float[6]; //아날로그핀 6종
void Start()
{
foreach (string str in SerialPort.GetPortNames()) {
Debug.Log(string.Format("Existing COM port: {0}", str));
}
my_serial = new SerialPort(portName, baudRate);
try
{
my_serial.DataReceived += DataReceivedHandler;
my_serial.ReadTimeout = 100;
my_serial.Open();
Debug.Log("open");
}
catch (System.TimeoutException e)
{
Debug.Log("Could not open serial port: " + e.Message);
}
Debug.Log("시작");
ThreadStart th = new ThreadStart(work); //1.work메소드를 위임.
t = new Thread(th); //2.쓰레드생성.
t.Start(); //3.시작
Debug.Log("끝!");
}
private Thread t;
static private string RawData = "";
public static void work()
{
while (my_serial.IsOpen)
{
//Debug.Log("my_serial.IsOpen:" + my_serial.IsOpen);
string value = "";
try
{
value = my_serial.ReadLine(); //Read the information
my_serial.ReadTimeout = SerialPort.InfiniteTimeout; //타임아웃설정을 길게잡아야 짧게 기다리고 포기하지 않는다.
//nano쪽 시리얼 통신속도가 느려서 unity가 기다리지 않고 포기하는 상황이 자주 발생함
//value = my_serial.ReadByte();
//Debug.Log("my_serial.ReadLine():" + value);
value = value.Trim(); //remove whitespace around our values
RawData = value;
//Debug.Log("ReadLine():" + value);
}
catch (Exception e)
{
Debug.Log("Exception:" + e);
}
}
Debug.Log("work end");
}
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
Debug.Log("is working?");
Debug.Log(e);
}
private string tmp;
private void Update()
{
//Debug.Log("RawData:" + RawData);
if (RawData == "") return;
chars = RawData.Split('|');
if (chars.Length < 21) return;
for (int i = 0; i < 14; i++)
{
//Debug.Log("chars[i]:" + chars[i]);
digital_pins[i] = int.Parse(chars[i]);
}
for (int i = 0; i < 6; i++)
{
analog_pins[i] = float.Parse(chars[14 + i]) / 1023;
}
}
private string[] datas;
void process_data(string tmp)
{
//데이터를 처리합니다.
datas = tmp.Split('|');
//Debug.Log(datas[3]);
// x,y,s bt1,bt2,bt3,bt4
}
void OnApplicationQuit()
{
Debug.Log("Application ending after " + Time.time + " seconds");
t.Abort(); //강제종료
my_serial.Close();
}
}
- 통신 타임아웃을 무한으로 잡아놓으면. 통신이 끊기거나 무응답시 무한루프 발생.. 높은 값으로 고쳐야 합니다.
동권형 버전
- SerialCommunication.cs
using System;
using System.Threading;
using System.IO;
using System.IO.Ports;
using UnityEngine;
using DG.Tweening;
public class SerialCommunication : MonoBehaviour
{
private ServerState state;
private string[] availablePorts;
private int availableCount;
private int timeout = 1000;
private int length = 3;
private SerialPort port;
private Thread thread;
private SendData result;
public static SerialCommunication Instance
{
get;
private set;
}
private void Awake()
{
if (Instance == null)
{
Instance = this;
}
this.state = ServerState.ready;
}
private void Start()
{
StartCommunication();
}
private void OnDestroy()
{
StopCommunication();
}
// start serialport
private void StartCommunication()
{
this.availableCount = 0;
availablePorts = SerialPort.GetPortNames();
state = ServerState.start;
if(availablePorts.Length > 0)
{
StartAvablePort();
StartReadThred();
} else
{
Debug.Log("serial port : Not Avable Port");
}
}
// close
private void StopCommunication()
{
if (port != null && port.IsOpen)
{
state = ServerState.stop;
port.Close();
port = null;
}
if (thread != null && thread.IsAlive)
{
thread.Abort();
thread = null;
}
}
private void StartReadThred()
{
if (thread != null && thread.IsAlive)
{
thread.Abort();
thread = null;
}
thread = new Thread(ReadPort);
thread.Start();
}
// open serialport
private void StartAvablePort()
{
try
{
if (port != null && port.IsOpen) StopCommunication();
state = ServerState.running;
port = new SerialPort(this.availablePorts[this.availableCount]);
port.BaudRate = 2400;
port.DataBits = 8;
port.Parity = Parity.None;
port.Handshake = Handshake.None;
port.StopBits = StopBits.One;
port.Open();
port.ReadTimeout = timeout;
port.WriteTimeout = timeout;
SendSerial("success");
Debug.Log(string.Format("[OPEN] serial port : {0}", this.availablePorts[this.availableCount]));
}
catch (IOException ex)
{
Debug.Log(string.Format("[IOERROR] {0}, avable count : {1}", ex.Message, this.availableCount));
DOVirtual.DelayedCall(0.5f, NextPortOpen);
}
catch (Exception ex)
{
Debug.Log(string.Format("[ERROR] {0}", ex.Message));
}
}
// next port
private void NextPortOpen()
{
state = ServerState.start;
this.availableCount++;
if (this.availableCount >= this.availablePorts.Length)
{
this.availableCount = 0;
}
this.StartAvablePort();
}
// write
public void SendSerial(string message)
{
if (port.IsOpen)
{
string msg = message;
try
{
port.Write(msg);
msg = "";
}
catch (TimeoutException exception)
{
string exceptionMessage = exception.Message;
}
}
}
// read
private void ReadPort()
{
while(state == ServerState.running)
{
if (port != null && port.IsOpen)
{
string msg;
try
{
msg = port.ReadLine();
print(msg);
//if (!string.IsNullOrEmpty(msg))
//{
// print(msg);
// //result = new SendData(msg);
// //print(result.ToString());
// if(result.model == "DK")
// {
// 프로토콜이 맞지 않을 경우 다른 포트로 연결 시도
/**********************************************
state = ServerState.start;
DOVirtual.DelayedCall(1.0f, () =>
{
StopCommunication();
DOVirtual.DelayedCall(1.0f, () =>
{
StartReadThred();
NextPortOpen();
});
state = ServerState.running;
});
**********************************************/
// }
//}
}
catch (TimeoutException exception)
{
//Debug.Log(string.Format("[TIMEOUT ERROR] {0}", exception.Message));
}
catch (Exception exception)
{
Debug.Log(string.Format("[ERROR] {0}", exception.Message));
}
}
Thread.Sleep(500);
}
}
}
- SandData.cs
public struct SendData
{
public char start;
public string model;
public int num;
public char end;
public SendData(string model, int num)
{
this.start = '[';
this.model = model;
this.num = num;
this.end = ']';
}
public SendData(string data)
{
string newData = data.Replace("\r", "").Replace("\n", "").Replace(" ", "");
this.start = '[';
this.model = newData.Substring(1, 2);
this.num = int.Parse(newData.Substring(4, 1));
this.end = ']';
}
public override string ToString()
{
return string.Format("{0}{1}_{2}{3}", this.start, this.model, this.num, this.end);
}
}
- ServerState
public enum ServerState
{
ready,
start,
running,
stop
}