在物聯網 (IoT)、互動設計 或 遊戲開發 專案中,將硬體感測資料即時整合到軟體應用中,是實現豐富互動體驗的關鍵。Arduino 作為一款開源硬體平台,搭配各種感測器能提供豐富的物理世界資料。而要將這些資料高效且有結構地傳輸到 Unity 或 TouchDesigner 等軟體平台,JSON (JavaScript Object Notation) 格式是一個極佳的選擇。
JSON 是一種輕量級的資料交換格式,易於人閱讀和撰寫,也易於機器解析和生成。本文將詳細說明如何利用 ArduinoJson 套件,將 Arduino 感測資料序列化為 JSON 字串,透過 Serial (序列埠) 傳輸,並在 Unity 與 TouchDesigner 中成功反序列化,最終應用於你的遊戲或互動專案。
安裝 ArduinoJson 套件
要讓 Arduino 能夠處理 JSON 格式的資料,我們需要安裝 ArduinoJson 套件。
- 開啟 Arduino IDE。點擊左側工具列的「函式庫管理員 (Library Manager)」按鈕(通常是一個書架圖示)。
- 在 Library Manager 搜尋欄中輸入「ArduinoJson」。
- 找到由 Benoit Blanchon 開發的「ArduinoJson」套件。點擊其右側的「安裝 (Install)」按鈕,等待安裝完成。

撰寫 Arduino 程式碼
發送 JSON 字串資料
準備好 ArduinoJson 套件後,我們就可以開始撰寫 Arduino 程式碼,模擬即時感測資料並將其包裝成 JSON 格式字串,然後透過 Serial 埠印出。
首先,在程式碼開頭引入 ArduinoJson.h 函式庫,並建立一個 JsonDocument 變數 doc。這個 doc 物件將用來儲存我們組織的 JSON 資料結構。
1#include "ArduinoJson.h";
2
3JsonDocument doc;
在 setup() 函式中,啟動 Serial (序列埠) 通訊。Serial.begin() 的 Baud Rate(鮑率)設定為 9600。你可以根據專案需求調整此數值,但請確保 Arduino 與接收端(Unity 或 TouchDesigner)的 Baud Rate 設定一致。
1void setup() {
2 Serial.begin(9600);
3}
在 loop() 函式中,我們要建立一個 String 型別的 dataName 變數和一個 int 型別的 dataValue 變數,用來模擬感測資料。dataName 的內容固定為 "testData",而 dataValue 則會產生 0 到 99 之間的隨機整數,模擬即時變化的感測器讀數。
1void loop() {
2 String dataName = "testData";
3 int dataValue = random(0, 100);
4}
接著,我們將 dataName 和 dataValue 分別存入 doc 物件中的 name 和 value 兩個鍵 (Key)。JsonDocument 的操作方式類似於 C# 的字典 (Dictionary),方便地用鍵值對 (Key-Value Pair) 的形式組織資料。
1void loop() {
2 String dataName = "testData";
3 int dataValue = random(0, 100);
4
5 doc["name"] = dataName;
6 doc["value"] = dataValue;
7}
最後,使用 serializeJson() 函式將 doc 物件的內容序列化為 JSON 字串,並透過 Serial 物件印出。為了確保每一條 JSON 訊息都是獨立的一行,我們強制加入新的一行符號 (Serial.println())。同時,設定 200 毫秒的延遲 (delay(200)),控制資料發送頻率。
1void loop() {
2 String dataName = "testData";
3 int dataValue = random(0, 100);
4
5 doc["name"] = dataName;
6 doc["value"] = dataValue;
7
8 serializeJson(doc, Serial);
9 Serial.println();
10 delay(200);
11}
將以上所有程式碼整合,完整的 Arduino 程式碼如下所示 (原始碼下載):
1#include "ArduinoJson.h";
2
3JsonDocument doc;
4
5void setup() {
6 Serial.begin(9600);
7}
8
9void loop() {
10 String dataName = "testData";
11 int dataValue = random(0, 100);
12
13 doc["name"] = dataName;
14 doc["value"] = dataValue;
15
16 serializeJson(doc, Serial);
17 Serial.println();
18 delay(200);
19}
將這段程式碼燒錄至你的 Arduino 板後,開啟 Arduino IDE 的「序列埠監控視窗 (Serial Monitor)」。你會看到程式持續地印出類似 {"name":"testData","value":76} 格式的 JSON 字串。
其中,value 的數字會不斷變化,這代表你的 Arduino 已成功開始發送 JSON 格式的感測資料。
![]()
除了從 Arduino 發送 JSON 資料到 Unity 或 TouchDesigner,有時我們也需要反向操作:讓 Arduino 接收外部應用程式(例如 Unity 或 TouchDesigner)傳來的 JSON 字串,並從中解析出所需的控制指令或參數。這對於實現更豐富的雙向互動至關重要。
接收 JSON 字串資料
首先,我們需要在程式碼中準備一個緩衝區 (buffer) 來暫存從序列埠陸續接收到的字元,直到完整的一條 JSON 訊息抵達。
在剛才建立的 JsonDocument 變數 doc 的部分,新增一個型別為 String 的變數 buffer。這個 buffer 將用來存放外部透過 Serial 埠輸入的 JSON 字串資料。
1#include "ArduinoJson.h";
2
3JsonDocument doc;
4String buffer;
接著,我們要建立一個名為 ParseJSON 的函式。這個函式將負責接收一個字串參數 raw(即完整的 JSON 字串),並從中提取我們需要的 name 和 value 資料。
在 ParseJSON 函式內部,我們透過 deserializeJson() 函式將 raw 這個 JSON 字串反序列化(解析)為可操作的 JsonDocument 物件 doc。一旦解析成功,我們就可以像操作字典 (Dictionary) 一樣,透過鍵名(例如 "name" 和 "value")來取出對應的值。這裡我們將 name 取為 const char* 型別,value 取為 long 型別,以確保資料型別的兼容性。
為了方便觀察解析結果,我們在提取資料後,立即將 name 和 value 的內容透過 Serial 埠印出來。
1void ParseJSON(String raw){
2 deserializeJson(doc, raw);
3
4 const char* name = doc["name"];
5 long value = doc["value"];
6
7 Serial.print("name: ");
8 Serial.println(name);
9 Serial.print("value: ");
10 Serial.println(value);
11}
最後,我們需要修改 loop() 函式的內容,使其同時具備發送與接收 JSON 資料的能力。loop() 將會持續檢查 Serial 埠是否有資料輸入,如果沒有,則繼續執行原有的發送 JSON 資料的邏輯。
1void loop() {
2 if (Serial.available() > 0) {
3 char msg = Serial.read();
4
5 if (msg == '\n'){
6 ParseJSON(buffer);
7 buffer = "";
8 }
9 else {
10 buffer += msg;
11 }
12 }
13 else {
14 String dataName = "testData";
15 int dataValue = random(0, 100);
16
17 doc["name"] = dataName;
18 doc["value"] = dataValue;
19
20 serializeJson(doc, Serial);
21 Serial.println();
22 }
23}
這個修改後的 loop() 函式實現了一個簡單的雙向通訊機制:當有資料從 Serial 埠傳入時,它會優先處理接收和解析。如果沒有接收到資料,它則會繼續執行既有的隨機資料發送任務。這使得你的 Arduino 裝置能夠更彈性地與外部應用進行互動和資料交換 (原始碼下載)。
另外,因為程式的執行速度很快,我們可另外用 delay() 函式來減慢執行速度,以方便觀察改動結果。

完整的 Arduino 用 Serial 與 JSON 實現的雙向溝通程式碼如下:
1#include "ArduinoJson.h";
2
3JsonDocument doc;
4String buffer;
5
6void setup() {
7 Serial.begin(9600);
8}
9
10void loop() {
11 if (Serial.available() > 0) {
12 char msg = Serial.read();
13
14 if (msg == '\n'){
15 ParseJSON(buffer);
16 buffer = "";
17 }
18 else {
19 buffer += msg;
20 }
21 }
22 else {
23 // 生成與發送模擬資料 (要發送的感測資料寫在這邊)
24 String dataName = "testData";
25 int dataValue = random(0, 100);
26
27 doc["name"] = dataName;
28 doc["value"] = dataValue;
29
30 serializeJson(doc, Serial);
31 Serial.println();
32 }
33}
34
35void ParseJSON(String raw){
36 deserializeJson(doc, raw);
37
38 // 接收與解析模擬資料 (要執行接收命令的功能寫在這邊)
39 const char* name = doc["name"];
40 long value = doc["value"];
41
42 Serial.print("name: ");
43 Serial.println(name);
44 Serial.print("value: ");
45 Serial.println(value);
46}
接下來,我們將展示如何在 Unity 和 TouchDesigner 中接收、解析、編碼、發送 JSON 資料,來和 Arduino 進行雙向溝通。
Unity 整合程式設計
接收 JSON 感測資料
在 Unity 中接收 Arduino 透過 Serial 傳輸的 JSON 資料,我們可以利用第三方套件來簡化開發流程。這裡我們推薦使用改良過的 Ardity 套件,它能方便地處理 Serial 埠通訊。
- 啟動你的 Unity 專案。在頂部選單中,點擊
Edit > Project Settings。 - 在
Project Settings視窗中,展開「Player」選項,然後點擊「Other Settings」。在「Configuration」區塊下,將「Api Compatibility Level」設定為.NET Framework。這是確保 Unity 能夠正確解析 JSON 和使用某些 .NET 函式庫的必要設定。

- 接下來,從頂部選單中點擊
Window > Package Manager。 - 在 Package Manager 視窗的左上角,點擊
+按鈕,然後選擇「Install package from git URL…」。

- 在彈出的輸入框中,複製並貼上下方的 Ardity 套件的 GitHub URL,然後點擊「Add」進行安裝。請注意,Unity 從 GitHub 下載套件需要你的電腦預先安裝 Git。如果尚未安裝 Git,請先完成安裝並重新啟動電腦以確保環境變數生效。
1https://github.com/tedliou/Ardity.git?path=Packages/ardity#2.0.1
Ardity 套件提供了一個方便的
SerialControllerPrefab,能快速建立與 Arduino 的 Serial 連線。在 Unity 的 Project 面板中,點擊右上角的「選項 (Options)」按鈕(通常是三個點或一個齒輪圖示),選擇「Show Package Folders」來顯示隱藏的套件檔案。展開
Packages資料夾,找到Ardity > Prefabs,然後將SerialControllerPrefab 拖曳到你的場景 (Hierarchy) 中。選取場景中的
SerialController物件。在 Inspector 面板中,你需要設定其屬性:- Port Name: 設定為你 Arduino 連接的序列埠名稱。Windows 通常為
COM加上數字(例如COM3),macOS 和 Linux 則為/dev/開頭的字串(例如/dev/tty.usbserial-123)。 - Baud Rate: 設定為
9600,與 Arduino 程式碼中的Serial.begin()設定一致。 - Max Unread Messages: 預設值為
10。你可以根據資料量和處理速度適當調高此值,讓系統能一次緩衝和接收更多訊息,避免資料遺失。
- Port Name: 設定為你 Arduino 連接的序列埠名稱。Windows 通常為

- 現在,我們要建立一個 C# 腳本來接收
SerialController傳來的 Arduino 訊息。在 Project 面板中,建立一個空的GameObject和一個新的 C# 腳本,兩者都命名為ArduinoController。將ArduinoController腳本拖曳到你剛建立的ArduinoController空物件上,將腳本附加到物件。

- 為了讓
SerialController將接收到的訊息傳遞給ArduinoController,你需要再次選取場景中的SerialController物件。在 Inspector 面板中,找到Message Listener欄位,然後將你場景中的ArduinoController空物件拖曳到此欄位中。

- 最後,編輯
ArduinoController腳本。在這個腳本中,我們將定義接收資料的結構,以及處理連線狀態和接收訊息的方法。- 建立一個
[System.Serializable]的InputData類別,其成員變數name(string) 和value(int) 需與 Arduino 發送的 JSON 鍵值("name"和"value")完全對應。這個類別將用於 JSON 的反序列化。 - 實作
OnConnectionEvent(bool state)方法,用於處理 Serial 連線的狀態變化。 - 實作
OnMessageArrived(string msg)方法,這是接收 Arduino 訊息的核心。在此方法中,我們將使用JsonUtility.FromJson<InputData>(msg)將接收到的 JSON 字串反序列化為InputData物件,然後透過Debug.Log印出其內容。
- 建立一個
1using UnityEngine;
2
3[System.Serializable]
4public class InputData {
5 public string name;
6 public int value;
7}
8
9public class ArduinoController : MonoBehaviour
10{
11 private void OnConnectionEvent(bool state)
12 {
13 Debug.Log($"連線狀態:{state}");
14 }
15
16 private void OnMessageArrived(string msg)
17 {
18 var data = JsonUtility.FromJson<InputData>(msg);
19 Debug.Log($"接收資料 {data.name}:{data.value}");
20 }
21}
執行 Unity 程式(點擊 Play 按鈕)。在 Unity 的 Console 視窗中,你將會持續看到 Arduino 傳來的 testData 和其隨機數值被印出 (原始碼下載)。

透過 data.name 和 data.value,你就可以在 Unity 中獲取 Arduino 感測資料的名稱和數值,進而將其應用於 遊戲開發、互動設計、資料視覺化等用途。
發送 JSON 執行命令
若想要反過來向 Arduino 發送命令呢?我們來設計一個範例吧!
假設 Arduino 會持續發送帶有 name 與 value 的 JSON 資料,但現在 name 的內容將由 Unity 決定,藉此模擬模式切換或遠端指令下達的應用情境。
首先,我們需要修改 Arduino 的程式碼,以便它能夠接收來自 Unity 的 name 值,並將其應用到發送的數據中。主要的更動將集中在第 5、24 與 41 行。
1#include "ArduinoJson.h";
2
3JsonDocument doc;
4String buffer;
5String mode = "testData";
6
7void setup() {
8 Serial.begin(9600);
9}
10
11void loop() {
12 if (Serial.available() > 0) {
13 char msg = Serial.read();
14
15 if (msg == '\n'){
16 ParseJSON(buffer);
17 buffer = "";
18 }
19 else {
20 buffer += msg;
21 }
22 }
23 else {
24 String dataName = mode;
25 int dataValue = random(0, 100);
26
27 doc["name"] = dataName;
28 doc["value"] = dataValue;
29
30 serializeJson(doc, Serial);
31 Serial.println();
32 }
33}
34
35void ParseJSON(String raw){
36 deserializeJson(doc, raw);
37
38 const char* name = doc["name"];
39 long value = doc["value"];
40
41 mode = name;
42}
程式碼解析:
- 第 5 行 (
String mode = "testData";):新增了一個String型別的mode變數,它將儲存當前 Arduino 要發送的name內容。預設值為"testData"。 - 第 24 行 (
String dataName = mode;):在loop()函式的發送邏輯中,dataName現在直接取自mode變數,而不是固定值。 - 第 41 行 (
mode = name;):在ParseJSON函式中,從接收到的 JSON 字串中解析出的name值,會被直接賦值給mode變數。這表示 Arduino 發送的name將會根據接收到的命令而改變。
完成 Arduino 程式碼修改後,將其燒錄到你的 Arduino 板 (原始碼下載)。
接著,我們修改 Unity 中的 ArduinoController.cs 腳本,使其能夠向 Arduino 發送 JSON 格式的命令。我們將新增一個公開的 SerialController 變數,並在 Update() 方法中加入鍵盤輸入偵測,以便在按下空白鍵時發送數據。
1using UnityEngine;
2
3[System.Serializable]
4public class InputData {
5 public string name;
6 public int value;
7}
8
9public class ArduinoController : MonoBehaviour
10{
11 public SerialController serialController;
12
13 private void Update()
14 {
15 if (Input.GetKeyDown(KeyCode.Space))
16 {
17 var send = new InputData
18 {
19 name = $"Hello World! {Time.time}",
20 value = 999999999
21 };
22 var json = JsonUtility.ToJson(send);
23
24 serialController.SendSerialMessage(json);
25 }
26 }
27
28 private void OnConnectionEvent(bool state)
29 {
30 Debug.Log($"連線狀態:{state}");
31 }
32
33 private void OnMessageArrived(string msg)
34 {
35 var data = JsonUtility.FromJson<InputData>(msg);
36 Debug.Log($"接收資料 {data.name}:{data.value}");
37 }
38}
C# 程式碼解析:
- 第 11 行 (
public SerialController serialController;):新增了一個公共變數serialController,這允許我們從 Unity Inspector 視窗將場景中的SerialController物件拖曳到此欄位,建立腳本與 Serial 控制器之間的連結。 - 第 13-26 行 (
private void Update() { ... }):在Update函式中,我們檢查KeyCode.Space是否被按下。如果按下,則建立一個InputData物件,其中name包含了動態的時間戳記,然後將其序列化為 JSON 字串,並透過serialController.SendSerialMessage()發送給 Arduino。
完成 C# 腳本修改後,回到 Unity Editor。你需要將場景中的 SerialController 物件拖曳到 ArduinoController 物件上,在 Inspector 視窗中,ArduinoController 腳本的 Serial Controller 欄位中。

最後,執行 Unity 程式。你會觀察到,每當你按下鍵盤上的空白鍵,Arduino 透過 Serial Monitor (或 Unity Console) 發送的 name 內容就會改變,顯示為類似 "Hello World! [時間戳記]" 的格式。這表示 Unity 成功向 Arduino 發送了命令,並且 Arduino 也成功接收並修改了其發送數據的模式 (原始碼下載)。

這個範例展示了如何實現 Unity 向 Arduino 發送 JSON 命令,進一步拓展了 Arduino 與 Unity 之間 雙向互動 的可能性。你可以將 InputData 的 name 或 value 用於控制 Arduino 上的 LED 亮滅、馬達轉速,或是切換不同的感測模式,實現更進階的 物聯網控制 與 實體運算 (Physical Computing) 應用。
TouchDesigner 互動媒體設計
接收 JSON 字串資料
TouchDesigner 作為一款強大的即時互動媒體開發工具,也能夠非常方便地接收和解析來自 Arduino 的 JSON 資料。我們將使用 Serial (DAT)、Select (DAT) 和 JSON (DAT) 等節點來處理資料流。
- 在 TouchDesigner 中,新增一個 Serial (DAT) 節點。這個節點用於與序列埠通訊。
- 設定
Row/Callback Format為One Per Line,這表示每讀到一個換行符號就視為一條完整的訊息。 - Port: 設定為你電腦連接 Arduino 的序列埠名稱(例如 Windows 的
COM3,macOS/Linux 的/dev/tty.usbserial-xxxx)。 - Baud Rate: 設定為
9600,與 Arduino 程式碼中的設定保持一致。
- 設定

- 新增一個 Select (DAT) 節點,並將其輸出連接到 Serial (DAT) 的輸入端。這個節點將用來選取我們需要的 JSON 資料行。
- 設定
Select Rows為by Index。 - Start Row Index 和 End Row Index 都設定為
1。由於Serial (DAT)的One Per Line模式會將接收到的最新訊息放在第二行(索引為 1),這樣設定可以確保我們總是選取到最新且完整的 JSON 字串。
- 設定

- 新增一個 JSON (DAT) 節點,並將其輸出連接到 Select (DAT) 的輸出端。這個節點負責將 JSON 字串反序列化。
- 設定
Output Format為Table。這會將 JSON 資料解析成一個表格形式,方便後續處理。
- 設定

- 最後,新增一個 DAT to (CHOP) 節點。這個節點會將表格資料轉換為 CHOP (Channel Operator) 頻道資料,這是 TouchDesigner 中最常使用的資料類型。
- DAT: 設定為上一步新增的
JSON (DAT)的名稱。 - Select Cols: 設定為
by Name。 - Start Col Name: 設定為
value(對應到 Arduino 發送的{"name":"testData","value":76}中的"value":76)。 - First Column is: 設定為
Names。這會將 JSON 中name鍵的值(即testData)作為 CHOP 頻道的名稱。
- DAT: 設定為上一步新增的

完成以上設定後,你將會在 DAT to (CHOP) 節點中看到一個名為 testData 的 CHOP 頻道,其數值會實時變動,顯示來自 Arduino 感測器的隨機資料 (原始碼下載)。
這個 testData 頻道及其數值現在就可以無縫地用於 TouchDesigner 中的各種 遊戲開發、互動設計、視覺化或裝置控制應用,為你的專案帶來實時的物理世界互動性。
發送 JSON 執行命令
如同 Unity 範例,TouchDesigner 也能夠向 Arduino 發送 JSON 格式的命令,實現即時互動控制。這能讓你在 TouchDesigner 的介面中控制 Arduino 的行為,例如切換感測模式或 觸發特定動作。
- 首先,在 TouchDesigner 網路編輯區新增一個 Button (COMP) 節點。
- 設定其
Button Type為Momentary。這表示當按鈕按下時觸發一次,放開時回到原始狀態,適合發送單次指令。
- 設定其
- 接著,新增一個 Null (CHOP) 節點,並將其連接到這個 Button (COMP) 的輸出端。Null (CHOP) 通常用於接收上一個節點的數據,並作為下一個節點的輸入,保持數據流的整潔。

- 現在,新增一個 CHOP Execute (DAT) 節點。這個節點允許你監聽 CHOP 數據的變化,並在特定條件下執行 Python 腳本。
- 在 CHOP Execute (DAT) 的參數面板中,設定
CHOPs為你剛才新增的Null (CHOP)的名稱。 - 在「Callbacks」頁籤下,啟用
Off to On(表示當 CHOP 頻道從 0 變為 1 時觸發,對應按鈕按下),並確保禁用其他模式(例如On To Off或While On),以避免重複執行。

- 編輯 CHOP Execute (DAT) 的程式碼。雙擊 CHOP Execute (DAT) 節點,打開其文字編輯器,並將以下 Python 程式碼貼入
onOffToOn函式中:
1import json
2
3def onOffToOn(channel, sampleIndex, val, prev):
4 data = {
5 "name": "Hello World! " + str(absTime.frame),
6 "value": 99999
7 }
8 json_str = json.dumps(data)
9 op("serial1").send(json_str, terminator="\n")
10 return
Python 程式碼解析:
import json:引入 Python 內建的json模組,用於處理 JSON 資料的序列化與反序列化。onOffToOn(channel, sampleIndex, val, prev):這是CHOP Execute (DAT)在Off to On事件發生時會自動呼叫的函式。data = { ... }:建立一個 Python 字典,其鍵名name和value與 Arduino 端的 JSON 結構 (InputData類別) 保持一致。name欄位使用了 absTime.frame 來取得當前 TouchDesigner 的幀數,使其每次發送都帶有獨特的識別。json_str = json.dumps(data):將 Python 字典data轉換為 JSON 格式的字串。op("serial1").send(json_str, terminator="\n"):這行是關鍵!它透過名稱為"serial1"的 Serial (DAT) 節點(假設你先前的 Serial (DAT) 節點名稱為serial1)發送 JSON 字串。terminator="\n"參數確保在發送的字串末尾加上換行符,這樣 Arduino 才能正確識別一條完整的 JSON 訊息。
完成設定後,點擊 TouchDesigner 網路編輯區中的 Button (COMP)。你會發現,這時 Arduino 透過 Serial Monitor(或 Unity Console)發送的 name 內容就會改變,顯示為 "Hello World! [時間戳記]" 的格式。這實現了與 Unity 類似的控制效果,證明 TouchDesigner 也能成功向 Arduino 發送指令並改變其行為模式 (原始碼下載)。

透過這些範例,你已掌握了 Arduino 與 Unity/TouchDesigner 之間高效且有結構的 JSON 數據雙向通訊。這為你的物聯網專案、互動藝術裝置、或是任何需要硬體與軟體協同運作的應用,開啟了無限的可能性。