개발 관련/SW, App 관련

Unity의 Serial 통신 사용하기

by 소서리스25 2023. 5. 6.
반응형

Unity의 Serial 통신 설정코드

 

여러 프로젝트를 진행하면서 가장 많이 공통적으로 들어간 사항이 Unity와 Arduino의 Serial 통신일 것이다.

주로 RS232 통신 방식으로 진행했으며, 9600 bps ~ 19200 bps 정도의 저속도 위주로 진행을 했었다.

따라서 이번 포스팅에서는 Unity와 Arduino 간 통신에 대해서 알아본다.

생각보다 간단하다.

 

먼저 전문적인 통신을 위한 언어와 기술이 들어간 방법이 아닌 나만의 간략한 수단과 방법이라 생각하면 되겠다. 그만한 기술은 가지고 있지 않아 필요하고 작동 잘되는 방법만 알기 때문에 대체적으로? 이해하는데 어렵지 않다.

 

현재 진행하고 있는 프로젝트도 있는데 이 방식으로 진행중이다.

 

통신이라 함은 보내기만 하는 단방향, 서로 주고받는 보내는 쪽, 받는 쪽의 양방향에 따라 코드가 달라진다. 하지만 보내는 통신의 정확성, 증명성을 위해 양방향을 하는 것이 좋다. H/W에서는 보내는 데이터 CheckSum을 보내기도 한다.

 

기본적인 코드는 다음과 같이 구성한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
using UnityEngine;
using System.Collections;
using System;
using System.IO;
using System.IO.Ports;
using UnityEngine.UI;
 
public class SerialConnect : MonoBehaviour
{
    [Header("Serial Connect")]
    SerialPort sp = new SerialPort();
 
    public int timeOut = 20;
    public bool serialState = false;
    public Text _debugText;
 
    bool _trans = false;
    bool _debugTextState = false;
 
    [Header("Setup File Read")]
    public string[] _readtextData;
    string _filePath = "./setup.ini";
 
    void Awake()
    {
        // 설정파일 불러오기
        ReadTextFile(_filePath);
    }
 
    void Start ()
    {
        //  디버그 화면표시 여부
        if (_readtextData[4].Equals("on"))
        {
            Cursor.visible = true;
            _debugTextState = true// DebugText("Log view on");
        }
        else
        {
            Cursor.visible = false;
            _debugText.gameObject.SetActive(false);
        }            
 
        if (serialState || _readtextData[0].Equals("on"))
        {
            SerialPort ();
            StartCoroutine(StartDelay());
 
            DebugText("Baudrate : " + _readtextData[1+ ", Port : " + _readtextData[2]);
        }
        else
        {
            DebugText("Serial connect off!!!");
        }
    }
    
    // comport 포트번호, bps 전송속도, 데이터 비트, 타임아웃
    void SerialPort()
    {
        sp.PortName = _readtextData[2];
        sp.BaudRate = int.Parse(_readtextData[1]);
        sp.DataBits = 8;        
        sp.Parity = Parity.None;
        sp.StopBits = StopBits.One;
        sp.ReadTimeout = int.Parse(_readtextData[3]);
        sp.WriteTimeout = int.Parse(_readtextData[3]);
 
        serialState = true;
 
        sp.Open();
    }
 
    IEnumerator StartDelay()
    {
        SendData("off"); // 만약에 켜져있었다면 off 초기화
 
        yield return new WaitForSeconds(2);
 
        DebugText("Switch Ready OK...");
 
 SendData("on");
    }
 
    void Update ()
    {
        if (_readtextData[0].Equals("on"))
        {
            if (sp.IsOpen)
            {
                _ReceiveData();
            }
            else
            {
                DebugText("Port not found.");
            }
        }
        else
        {
            DebugText("Not Connected,...");
        }
    }
 
    public void SendData(string sendData)
    {
        if (serialState)
        {
            sp.Write(sendData);
        }            
    }
 
 
    void _ReceiveData()
    {
        string temps = "";
 
        try {
            char tempB = (char)sp.ReadChar();
 
            while (tempB != 256)
            { 
                temps += (char)tempB; 
                tempB = (char)sp.ReadChar();
            }
        }
        catch (Exception e)
        {
            // blink
        }
 
        if(temps != "")
        {
            DelayReceiveData(temps);
        }
    }
 
    void DelayReceiveData(string data)
    {
        switch (data)
        {
            case "1":
                // "1" 받았을때 이곳에서 수신 처리하기
                break;
 
            case "2":
                // "2" 받았을때 이곳에서 수신 처리하기
                break;
        }
    }
 
    void OnApplicationQuit()
    {
        SendData("off");
        sp.Close();
    }
 
    void DebugText(string text)
    {
        if (_debugTextState)
            _debugText.text = text;
    }
 
    void ReadTextFile(string filePath)
    {
        StreamReader rd = new StreamReader(filePath);
 
        for (int i = 0; i < _readtextData.Length; i++)
        {
            _readtextData[i] = rd.ReadLine();
 
            if (_readtextData[i].Equals("")) break;
        }
 
        rd.Close();
    }
}
cs

 

스크립트를 Gameobject에 적용하면 다음과 같인 Inspector를 구성하게 된다.

설정파일의 수는 적절하게 일단 10개로 해 두자. 그리고 Canvas에서 적절히 긴 text 객체를 하나 만들어서 연결한다. 통신의 연결상태를 확인하기 위해서이다. 이 부분은 설정파일에서 on/off 할 수 있도록 되어 있다.

Serial Connect 코드의 Inspector 화면
Serial Connect 코드의 Inspector 화면

 

Serial 통신에서 가장 많이 조정하는 것이 comport 통신포트와 baudrate 통신속도, 그리고 timeout 정도가 되겠다. 따라서 이를 내부에 고정해 버리면 다른 PC에서 통신포트의 연결 순서가 바뀔 수 있기 때문에 외부 설정파일에서 조정해야 된다. Arduino는 주로 PC와는 USBtoSerial로 연결되기 때문에 다른 포트가 통신포트를 사용하고 있으면 변경해 주어야 하는데 그냥 설정파일로 하면 편하게 변경할 수 있기 때문에 그렇게 하는 것이 좋다.

설정파일은 일반 text 문서로 다음과 같이 간단하게 작성한다.

설정파일을 외부에서 작성하도록 함
설정파일을 외부에서 작성하도록 함

 

통신의 연결과 상관없이 작동여부희 확인이 되어야 하므로 통신 자체도 on/off 하면 좋을 것이다.

이제 Unity의 플레이버튼을 눌러보면 다음과 같이 설정파일을 불러올 것이다.

외부 설정파일에서 설정 데이터를 읽음
외부 설정파일에서 설정 데이터를 읽음

 

해당 설정을 각 SerialPort()에서 불러올 것이다.

 

이제 Unity에서의 설정은 대기로 두고 Arduino에서 Sketch로 코드를 만들 차례이다. 

그전에 위에서의 Unity와 Arduino 사이에 주고받는 문자가 있다.

 

Unity에서 보내는 문자는 임의로 만든 2가지 사항이다. H/W에서 처리하기 위함이었다.

"on"

"off"

 

그리고 Unity에서  받는 문자는 다음과 같다.

"1"

"2"

 

해당 부분의 처리 내용은 나중에 Debug Log에서 확인해 보자.

 

 

Arduino 설정하기

 

그러면 이번에는 Arduino의 Sketch의 코드를 아래와 같이 하자.

Arduino의 자체 led는 13번 핀과 동일하게 작동한다. 따라서 Arduino의 작동 여부를 확인하기 위해 13핀으로 연결한다.

즉, "on" 신호가 들어오면 led가 켜지고 "off"신호가 들어오면 led가 꺼진다.

코드를 작성 후 반드시 v 버튼을 눌러 에러가 없는지 확인해 보자.

Arduino에서 코드의 오류가 없는지 확인
Arduino에서 코드의 오류가 없는지 확인

 

위의 Arduino코드는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const int _led = 13;
 
bool _state = true;
String _inString;
 
void setup()
{
  pinMode(_led, OUTPUT);
 
  Serial.begin(9600);
  Serial.println("Start~");
}
 
void loop()
{
  if (Serial.available() > 0)
  {
    _inString = Serial.readString();
 
    if(_inString == "on")
      DelayCheck(true);
    else if(_inString == "off")
      DelayCheck(false);
  }    
  
  delay(20);
}
 
void DelayCheck(bool mode)
{
  if(mode)
    digitalWrite(_led, HIGH);
  else
    digitalWrite(_led, LOW);
}
 
cs

 

Arduino를 연결하니 comport를 4번으로 잡았다. 따라서 설정파일의 com3 > com4로 변경하였다.

우선 Sketch의 시리얼 모니터로 자체적인 상태 확인이 가능하다. 

메뉴의 툴 > 시리얼 모니터

자체 Serial Monitor에서 확인해 보기
자체 Serial Monitor에서 확인해 보기

 

그러면 Unity에서 StartDelay() 스크립트를 보면 시작할 때 "off" 신호를 보낸 뒤에 2초 후에 다시 "on"을 보내게 해 놨었다.

Unity를 실행해 보고 디버그를 살펴보자.

Unity와 통신이 제대로 되었는지 확인
Unity와 통신이 제대로 되었는지 확인

 

속도와 포트가 정상적으로 표시되었다.

그러면 Arduino에서 led가 켜지고 꺼지는지 확인해 보자.

 

* 좁은 책상에 USB 연결선까지 짧아 손으로 촬영... 많이 흔들린다..-_-;;

처음 연결하면 연결상태로 Led가 아주 짧게 깜빡인다. 그다음 "on"시 켜지고 "off" 꺼지게 된다.

 

 

반응형

댓글