2023. 12. 8. 11:11ㆍSKKU DT
아두이노의 구성
Digital In/Out과 Analog In을 주로 사용할 것이다.
전원은 컴퓨터에서 오는 USB전원을 받게 되거나 소형 건전지로 공급할 수 있다.
BreadBoard
두 줄과 다섯 줄로 이루어져있다. 두 줄은 전원선으로, 버스(Bus)라고 한다.
로그인을 하고
회로를 새로 생성한다.
오른쪽에서 필요한 부품들을 꺼내서 쓸 수 있다.
전원의 공급은 3.3V 또는 5V에서 시작하고 GND(그라운드)로 끝나면 된다.
LED 켜기 예시. 마우스로 전선을 만들 수 있고 오른쪽 위에 시뮬레이션 시작을 누르면 시뮬레이션이 된다.
저항이 없어서 LED가 깨졌다.
저항을 달면 LED가 깨지지 않는다.
브레드 보드를 이용해서 불을 켜보자.
위와 같은 방식으로 브레드보드를 이용해서 LED에 불을 켤 수 있다.
이해하기 쉽게 빨간 선을 추가하면 회로 모양이 나오는 것을 볼 수 있다.
LED를 주황색으로 바꾸고 누름 버튼을 추가하면 누를 때마다 LED가 꺼지는 것을 볼 수 있다.
슬라이드 스위치로 LED를 켜기 위해서는 아래와 같은 배치를 할 수 있다.
마찬가지로 할 수 있는 LED 2개 불켜기(병렬연결)
아두이노 통합 개발 환경(Arduino IDE)
Software에서 다운로드한다.
Arduino IDE를 설치하면 다음과 같은 화면이 나타난다.
왼쪽 위 체크표시는 컴파일 버튼, 화살표는 아두이노에 업로드 버튼, 그 옆 드롭다운 박스는 시리얼 포트를 선택하는 부분이다. 이후 컴퓨터와 아두이노를 연결하면 장치관리자에서 통신 포트를 확인할 수 있다.
컴퓨터와 아두이노가 연결되면 장치관리자에 Port가 뜨며 Arduino IDE에서 Arduino Uno를 해당 포트에 연결한다.
[File] - [Examples] - [01.Basics] - [Blink] 하면 아두이노의 불빛이 깜빡거린다.
[File] - [Examples] - [01.Basics] - [Fade] 하면 아두이노의 불빛이 켜져있는 상태로 깜빡거리지 않는다.
실제로 LED 램프 설치하기
스케치에서 13pin이 중요한 점이다. LED 등의 긴 부분이 +, 짧은 부분이 - 이다.
void setup() {
pinMode(13, OUTPUT); //13번 핀을 출력으로 설정
}
void loop() {
digitalWrite(13, HIGH); //LED 켜기
delay(1000); //1초 기다리기
digitalWrite(13, LOW); //LED 끄기
delay(1000); //1초 기다리기
}
HIGH 뜻 : 전력이 들어갔다
파워 5V 또는 3.3V 대신 13번 핀에서 전원이 나와서 LED가 깜빡깜빡한다.
유니티에서 시리얼 통신으로 연결하기
스케치에서 코드 넣기
int ledPin = 13; //LED를 연결할 핀 번호
void setup() {
pinMode(ledPin, OUTPUT); //13번 핀을 출력으로 설정
Serial.begin(19200); //시리얼 통신 시작 (바우드레이트 9600)
}
void loop() {
if(Serial.available() > 0){ //시리얼 데이터가 들어오면
char receivedChar = Serial.read(); //데이터 읽기
if(receivedChar == '1'){
digitalWrite(ledPin, HIGH); //LED 켜기
}
else if(receivedChar == '0'){
digitalWrite(ledPin, LOW); //LED 끄기
}
}
}
유니티에서 스크립트 생성하기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO.Ports;
public class ArduinoController : MonoBehaviour
{
SerialPort serialPort = new SerialPort("COM8", 19200); //COM 포트와 바우드레이트 설정
private bool ledState = false;
void Start()
{
serialPort.Open(); //시리얼 포트 열기
serialPort.ReadTimeout = 50; //읽기 타임아웃 설정
}
private void Update()
{
if(Input.GetKeyDown(KeyCode.Space)) //스페이스바가 눌리면
{
ledState = !ledState; //LED 상태 토글
serialPort.Write(ledState ? "1" : "0"); //아두이노에 데이터 보내기
}
}
private void OnDestroy()
{
if(serialPort != null && serialPort.IsOpen)
{
serialPort.Close(); //시리얼 포트 닫기
}
}
}
Systme.IO.Ports 네임스페이스에서 오류가 발생한다면, [Project Settings] - [Player] - [Other Settings] - [Configuration]에서 [Scripting Backend]와 [Api Compatibility Level]을 아래와 같이 수정하면 된다.
using System.IO.Ports
시리얼 포트를 열기 위해서 두 설정을 수정하면 된다.
아두이노를 컴퓨터와 연결하고 유니티 상에서 게임 오브젝트에 스크립트를 넣고 실행하고 [Space] 버튼을 누르면 LED가 켜졌다 꺼졌다 한다.
응용) LED 3개 번갈아가면서 켜기
int ledPin =13; // LED를 연결할 핀 번호
int newledPin =12; // LED를 연결할 핀 번호
int newnewledPin =11; // LED를 연결할 핀 번호
int i = 0;
void setup() {
// put your setup code here, to run once:
pinMode(ledPin, OUTPUT); // LED 핀을 출력으로 설정
pinMode(newledPin, OUTPUT); // NEWLED 핀을 출력으로 설정
pinMode(newnewledPin, OUTPUT); // NEWLED 핀을 출력으로 설정
Serial.begin(19200); // 시리얼 통신 시작(바우드레이트 9600)
}
void loop() {
// put your main code here, to run repeatedly:
if (Serial.available() > 0) { //시리얼 데이터가 들어오면
i++;
char receivedChar = Serial.read(); // 데이터 읽기
if(i % 3 == 1) {
digitalWrite(ledPin, HIGH); // LED 켜기
digitalWrite(newledPin, LOW); // LED 끄기
digitalWrite(newnewledPin, LOW); // LED 끄기
}
else if (i % 3 == 2){
digitalWrite(ledPin, LOW); // LED 끄기
digitalWrite(newledPin, HIGH); // LED 켜기
digitalWrite(newnewledPin, LOW); // LED 끄기
}
else if(i % 3 == 0) {
digitalWrite(ledPin, LOW); // LED 끄기
digitalWrite(newledPin, LOW); // LED 끄기
digitalWrite(newnewledPin, HIGH); // LED 켜기
}
}
}
조이스틱 값 표시하기
스케치에서 아래와 같은 스크립트를 생성한다.
Serial Monitor를 켜면 출력 값을 볼 수 있다.
Serial Monitor 오른쪽에 baud를 19200으로 맞춘다.
int x = A0;
int y = A1;
void setup(){
Serial.begin(19200);
}
void loop(){
int x_val = analogRead(x);
int y_val = analogRead(y);
Serial.print("x 좌표는 : ");
Serial.print(x_val);
Serial.print(" / ");
Serial.print("y 좌표는 : ");
Serial.println(x_val);
delay(1000);
}
조이스틱의 좌표가 출력된다. 총 5개의 핀이 있으며 각각(조이스틱-아두이노 순서로) GND-GND, 5V-5V, URX-A0 ,URY-A1, SW-03에 연결했다.
중립일 때 (512, 512)가 출력된다.
2초마다 값이 변하는 아두이노의 n 값을 유니티에 가져오기
아두이노 스케치에 아래의 스크립트를 생성한다.
int n = 0;
void setup(){
Serial.begin(19200);
}
void loop(){
Serial.print(n);
Serial.println("sec");
delay(2000);
n++;
}
유니티에서는 아래와 같은 스크립트를 생성한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO.Ports;
using System;
public class SerialReader : MonoBehaviour
{
SerialPort serialPort;
public string portName = "COM8"; //시리얼 포트 이름
public int baudRate = 19200; //보드레이트
void Start()
{
serialPort = new SerialPort(portName, baudRate);
try
{
serialPort.Open(); //시리얼 포트 열기
}
catch(Exception e)
{
Debug.LogError("Serial port open error: " + e.Message);
}
}
void Update()
{
if(serialPort != null && serialPort.IsOpen)
{
try
{
string data = serialPort.ReadLine();
Debug.Log("Data received: " + data);
}
catch(TimeoutException) { }
}
}
private void OnDestroy()
{
if(serialPort != null && serialPort.IsOpen)
{
serialPort.Close(); //시리얼 포트 닫기
}
}
}
게임 오브젝트에 스크립트를 넣고 실행을 하면 Console 창에 아래와 같은 출력 값이 나온다. 이때, Arduino IDE를 끄지 않으면 엑세스 거부 오류가 뜨고 IDE를 꺼야 정상적으로 출력이 된다.
아두이노에서 버튼 값 가져오기
스케치에서 아래의 스크립트를 입력한다.
const int buttonPin = 2; //버튼이 연결된 핀 번호
void setup(){
pinMode(buttonPin, INPUT); //버튼 핀을 입력으로 설정
Serial.begin(19200); //시리얼 통신 시작
}
void loop(){
int buttonState = digitalRead(buttonPin); //버튼 상태 읽기
if(buttonState == HIGH){ //버튼 눌렸을 경우
Serial.println("jump"); //'jump' 메시지 보내기
delay(500); //디바운싱을 위한 딜레이
}
}
유니티 스크립트는 아래와 같다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO.Ports;
public class BoxJump : MonoBehaviour
{
SerialPort serialPort = new SerialPort("COM8", 19200);
public float jumpForce = 5f;
void Start()
{
serialPort.Open(); //시리얼 포트 열기
serialPort.ReadTimeout = 50;
}
private void Update()
{
if (serialPort.IsOpen)
{
try
{
string message = serialPort.ReadLine();
if (message.Contains("jump"))
{
GetComponent<Rigidbody>().AddForce(0, jumpForce, 0, ForceMode.VelocityChange);
}
}
catch (System.TimeoutException)
{
//메시지가 없을 경우 예외 처리
}
}
}
private void OnDestroy()
{
if(serialPort != null && serialPort.IsOpen)
{
serialPort.Close(); //시리얼 포트 닫기
}
}
}
Rigidbody를 넣은 큐브에 해당 스크립트를 넣으면 버튼을 누를 때마다 큐브가 Y축 방향으로 점프를 하는 것을 볼 수 있다.
조이스틱을 움직여서 실제로 큐브 움직이기
실시간으로 들어오는 데이터를 나눠서 숫자 값만 들어오게 한 후 유니티의 transform.position에 Update 함수에서 적용되게 하면 될 것 같다.
문자열 중 숫자 값만 받아올 수 있게 하는 함수를 추가하였다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO.Ports;
using System;
using System.Text.RegularExpressions;
public class CubeMove : MonoBehaviour
{
SerialPort serialPort = new SerialPort("COM8", 19200);
public float moveX;
public float moveZ;
void Start()
{
serialPort.Open(); //시리얼 포트 열기
serialPort.ReadTimeout = 50;
}
string ExtractNumbers(string message)
{
string pattern = @"\d+";
MatchCollection matches = Regex.Matches(message, pattern);
string result = string.Join("", matches);
return result;
}
private void Update()
{
if (serialPort.IsOpen)
{
try
{
string message = serialPort.ReadLine();
string result = ExtractNumbers(message);
Debug.Log(result);
if (message.Contains("x"))
{
transform.position = GetComponent<Transform>().position;
transform.position = new Vector3(Convert.ToInt32(result)*(float)0.0000001, transform.position.y, transform.position.z);
}
else if(message.Contains("y 좌표는 : "))
{
transform.position = GetComponent<Transform>().position;
transform.position = new Vector3(Convert.ToInt32(result), transform.position.y, transform.position.z);
}
}
catch (System.TimeoutException)
{
//메시지가 없을 경우 예외 처리
}
}
}
private void OnDestroy()
{
if (serialPort != null && serialPort.IsOpen)
{
serialPort.Close(); //시리얼 포트 닫기
}
}
}
숫자값만 문자열로 받아오는 데에 성공하였다. 하지만 십만 자리 숫자로 들어오기 때문에 나누는 작업이 필요하다.
...
스케치 정답
//조이스틱 x축 y축 연결 핀 설정
const int joystickXPin = A0;
const int joystickYPin = A1; //y축 추가
void setup(){
//시리얼 통신 시작
Serial.begin(9600);
}
void loop(){
//조이스틱 x축과 y축 값 읽기
int joystickXValue = analogRead(joystickXPin);
int joystickYValue = analogRead(joystickYPin);
//조이스틱 X축 값에 따라 조건 검사
if(joystickXValue <= 300){
//X축 값이 300 이하이면 "A" 전송
Serial.println("A");
}
else if(joystickXValue >= 700){
//X축 값이 700 이상이면 "D" 전송
Serial.println("D");
}
//조이스틱 Y축 값에 따라 조건 검사 추가
if(joystickYValue <= 300){
//X축 값이 300 이하이면 "W" 전송
Serial.println("W");
}
else if(joystickYValue >= 700){
//X축 값이 700 이상이면 "S" 전송
Serial.println("S");
}
//조금의 지연을 추가하여 무한 루프를 받자
delay(100);
}
C# 정답 (Switch문 대소문자 구별도 중요하다)
using System;
using System.IO.Ports;
using UnityEngine;
public class ArduinoInputReader : MonoBehaviour
{
SerialPort serialPort;
public string portName = "COM8"; // 시리얼 포트 이름
public int baudRate = 19200; // 보드레이트
public float moveSpeed = 20f;
void Start()
{
serialPort = new SerialPort(portName, baudRate);
try
{
serialPort.Open(); // 시리얼 포트 열기
}
catch (Exception e)
{
Debug.LogError("Serial port open error: " + e.Message);
}
}
void Update()
{
if (serialPort != null && serialPort.IsOpen)
{
try
{
string data = serialPort.ReadLine(); // 시리얼 포트에서 데이터 읽기
ProcessData(data.Trim()); // 데이터 처리
}
catch (TimeoutException) { }
}
}
void ProcessData(string data)
{
switch (data)
{
case "A":
// 'a' 데이터 처리
transform.Translate(Vector3.left * Time.deltaTime);
Debug.Log("Received 'A'");
break;
case "D":
// 'd' 데이터 처리
transform.Translate(Vector3.right * Time.deltaTime);
Debug.Log("Received 'D'");
break;
case "S":
// 's' 데이터 처리
transform.Translate(Vector3.back * Time.deltaTime);
Debug.Log("Received 'S'");
break;
case "W":
// 'w' 데이터 처리
transform.Translate(Vector3.forward * Time.deltaTime);
Debug.Log("Received 'W'");
break;
}
}
void OnDestroy()
{
if (serialPort != null && serialPort.IsOpen)
{
serialPort.Close(); // 시리얼 포트 닫기
}
}
}
다행히 잘 움직인다. 조금 버벅이는 느낌이 있다.
'SKKU DT' 카테고리의 다른 글
[SKKU DT] 31일차 -유니티 네트워크 -파이어베이스(Firebase) 실시간 데이터베이스, Storage (0) | 2023.12.12 |
---|---|
[SKKU DT] 30일차 -유니티 네트워크 -파이어베이스(Firebase) Authentication 로그인, 익명 로그인 (1) | 2023.12.11 |
[SKKU DT] 28일차 -유니티 네트워크(System.IO, JSON, Xml, CSV), API, 에셋 번들 (2) | 2023.12.07 |
[SKKU DT] 27일차 -유니티 미니 프로젝트(로봇 팔 시뮬레이션)(2) (1) | 2023.12.05 |
[SKKU DT] 26일차 -유니티 미니 프로젝트(로봇 팔 시뮬레이션) (1) | 2023.12.05 |