底下三篇文章是要測試Arduino透過串列埠傳輸資料到PC,藉以控制Unity專案上的物件。
想法:
(1)Arduino以固定(或幾乎固定)時間間隔,每次傳送一個字串(一行),裡面包含幾個相關數據。
(2)Unity以固定(或幾乎固定)時間間隔,每次讀取一行字串,再拆解取得相關數據。
(3)Arduino端以較短的時間間隔傳送資料,Unity端以較長的時間間隔讀取資料,為避免資料累積造成lag,Unity端可以將多的資料丟棄。
(4)Unity端利用UI上的下拉式選單、按鈕,藉以設定串列埠名稱、開啟連線、結束連線等功能。
(5)串列埠連線需常駐,不會因Unity端變更場景而造成斷線或需重新連線。
因測試文章較長,亦可直接看最後第三篇的作法。
=====================================================================
在第一篇文章中,我們首先建立Arduino與Unity專案的串列埠連線的基本功能:
利用Arduino連接搖桿產生數據,一開始為簡化問題,先只傳送按鈕數據(1個字元),按鈕沒按下時傳送1,按下時傳送0。以Serial.println傳送。
S-K接Pin12
S-X接A0、S-Y接A1
Arduino程式:
-----------------------------------------------------
int SK, SX, SY;
String SSK, SSX, SSY;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
pinMode(12, INPUT);
}
void loop() {
// put your main code here, to run repeatedly:
SK=digitalRead(12);
SX=analogRead(A0);
SY=analogRead(A1);
SSK=String(SK);
SSX=String(SX);
SSY=String(SY);
Serial.println(SSK);
delay(5);
}
=========================================
先利用Arduino IDE的Serial Monitor檢查資料是否正確
按鈕未按下時,傳輸1,按鈕按下時,傳輸0
測試完,記得關閉Serial Monitor。
==========================================
==========================================
Unity端
在場景中放置一個Cube
調整Main Camera高度與角度
目的:當Arduino端連接的搖桿未按下時,Cube不旋轉。搖桿按下時,Cube旋轉。
先在專案Asset視窗中新建一個C#檔ComData.cs,設定共用變數
C#程式:
-----------------------------------------------------
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
static class ComData
{
public static int R=1;
}
=========================================
再建立一個腳本Ctrlmotion.cs,附加在Cube上。
C#程式:
-----------------------------------------------------
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Ctrlmotion : MonoBehaviour
{
public float rspeed =30f;
void Start()
{
}
// Update is called once per frame
void Update()
{
if (ComData.R==0)
{
transform.Rotate(Vector3.up * Time.deltaTime*rspeed);
}
}
}
----------------------------------------------------------------
可在inspector視窗調整rspeed數值,改變旋轉速度。
==========================================
接著在Hierarchy視窗中加入Canvas,在Canvas中加入2個按鈕、1個下拉式選單。
修改物件名稱如下
在下拉式選單的inspector視窗中,將Options先刪除。
其選項可在C#程式中,由SerialPort.GetPortNames()取得。
但SerialPort.GetPortNames()的資料型態是string陣列,
需再轉為List。
==========================================
接著建立一個腳本Serialsetup.cs,附加在EventSystem上。
C#程式:
-----------------------------------------------------
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.IO.Ports;
public class Serialsetup : MonoBehaviour
{
public static SerialPort sp = new SerialPort();
private string V = "1";
public GameObject Canv, B_C, B_D;
public Dropdown Dp1;
private string PN = null;
private string[] Com_name=null;
private List<string> list;
private bool b_conv = true;
// Start is called before the first frame update
public void AddSerial()
{
PN = Dp1.options[Dp1.value].text;
if (!sp.IsOpen)
{
B_C.SetActive(true);
B_D.SetActive(false);
}
}
public void Connect()
{
try
{
sp.BaudRate = 9600;
sp.Parity = Parity.None;
sp.DataBits = 8;
sp.StopBits = StopBits.One;
sp.PortName = PN;
sp.Open();
sp.ReadTimeout = 1;
B_D.SetActive(true);
B_C.SetActive(false);
}
catch (System.Exception)
{
}
}
public void Disconnect()
{
sp.Close();
B_C.SetActive(true);
B_D.SetActive(false);
}
void Start()
{
B_C.SetActive(false);
B_D.SetActive(false);
Com_name = SerialPort.GetPortNames();
list= new List<string>(Com_name);
Dp1.AddOptions(list);
}
// Update is called once per frame
void Update()
{
if (sp.IsOpen)
{
if (Input.GetKeyDown(KeyCode.Escape))
{
b_conv = !b_conv;
Canv.SetActive(b_conv);
}
}
else
{
Canv.SetActive(true);
}
}
private void FixedUpdate()
{
if (sp.IsOpen)
{
try
{
V = sp.ReadLine();
sp.DiscardInBuffer();
}
catch (System.Exception)
{
}
}
if (V == "1")
{
ComData.R = 1;
}
else if (V == "0")
{
ComData.R = 0;
}
}
}
----------------------------------------------------------------
這邊須注意,如果無法加入System.IO.Ports,檢查Api Compatibility Level
是否設定為.Net 4.x
在EventSystem的inspector視窗中,在加入的腳本元件中,拉入對應的物件:
在Button1的inspector視窗中,將EventSystem拉入,選擇對應的function:
Button2和下拉式選單也是相同設定:
說明:
一開始先將2個Button設定為不執行;
B_C.SetActive(false);
B_D.SetActive(false);
取得串列埠PortName(字串陣列),轉換成List,再加入成為下拉式選單的選項:
Com_name = SerialPort.GetPortNames();
list= new List<string>(Com_name);
Dp1.AddOptions(list);
當下拉式選單所選的項目被改變時,執行AddSerial()
首先取得被選項的文字(字串)
PN = Dp1.options[Dp1.value].text;
當串列埠未開啟時,將Button(Connect)設為可使用,Button(Disconnect)設為不可使用。
反之,則不改變2個Button的狀態。
if (!sp.IsOpen)
{
B_C.SetActive(true);
B_D.SetActive(false);
}
當Button(Connect)可使用時,其被按下,則執行Connect()。
設定串列埠的一些參數,並開啟連線。並改變2個Button的狀態。
當Button(Disconnect)可使用時,其被按下,則執行Disconnect()。
其關閉連線,並改變2個Button的狀態。
使用FixedUpdate(),固定時間間隔執行。
當串列埠開啟時,讀取一行字串:
V = sp.ReadLine();
並將inBuffer的資料捨棄。
sp.DiscardInBuffer();
如果該字串內容是"1",則將ComData的共用參數R設為1;
如果該字串內容是"0",則將ComData的共用參數R設為0。
此外,在Update()執行鍵盤input判斷,當連線開啟時,按下Esc可使介面隱藏或出現。
===============================================================
執行結果:
下拉式選單中選擇PortName
在實體搖桿,按下搖桿時,Cube旋轉;放開則Cube不旋轉。
沒有留言:
張貼留言