どうも、土鍋です。
IVRCに向けた開発を行っている際に使用した「Unityと複数台のESP32間でのUDP通信」の知見をまとめました~。
Unityがサーバー側、ESP32がクライアント側で、Unityからデータを全部のESP32に向けて送信しています。また、今回のIVRC作品では使用しませんでしたが、一応ESP32側からのデータの受信もできます。
※ルーターでIPアドレスの固定を行ってください。
ちなみに、ESP32側のコードは@suzakutakumi3 にお願いしました。
サーバー側(Unity)
UniTaskで非同期処理を行っていて、一応送受信できる設計になっています。
using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using Cysharp.Threading.Tasks; using UnityEngine; namespace UDP { public class UDPServer { private UdpClient udpClient; public UDPServer(int port) { udpClient = new UdpClient(port); } public async UniTaskVoid Send(string data, IPEndPoint endPoint, CancellationToken token = default) { byte[] message = Encoding.UTF8.GetBytes(data); await udpClient.SendAsync(message, message.Length, endPoint); Debug.Log("Send:\"" + data + "\" To:" + endPoint.Address + "," + endPoint.Port); } public async UniTask<string> Receive(CancellationToken token = default) { var result = await udpClient.ReceiveAsync(); IPEndPoint endPoint = result.RemoteEndPoint; Debug.Log("Receive:" + endPoint.Address + "," + endPoint.Port); byte[] getByte = result.Buffer; string data = Encoding.UTF8.GetString(getByte); return data; } } }
Sendメソッドはstring型で送信データ、IPEndPoint型で送信先を設定して送信します。 Receiveメソッドはデータを受け取るまで待機して、データが送られてきたらstringをリターンします。
クライアント側(ESP32)
UDPLibrary.h
#pragma once #include <WiFi.h> #include <WiFiUdp.h> class UDPLib { private: WiFiUDP wifiUdp; String host; int send_port; int receive_port; public: void begin(String ip, int send_p, int rec_p); void send(String); String read(); };
UDPLibrary.ino
#include "UDPLibrary.h" void UDPLib::begin(String ip, int send_p, int rec_p) { host = ip; send_port = send_p; receive_port = rec_p; wifiUdp.begin(receive_port); } void UDPLib::send(String text) { byte Data[256]; char hostC[256]; host.toCharArray(hostC,text.length()); Serial.println(hostC); text.getBytes(Data, text.length() + 1); wifiUdp.beginPacket(hostC, send_port); wifiUdp.write(Data, text.length() + 1); //10進数のASCiiで送信される wifiUdp.endPacket(); } String UDPLib::read() { char x[512] = ""; if (wifiUdp.parsePacket()) { for (int i = 0; i < 512; i++) { int v = wifiUdp.read(); if (v == -1) { x[i] = '\0'; break; } x[i] = v; } } String r=x; return r; }
テスト用コード
以下はテスト用に作成したコードです。
適当な空のオブジェクト作ってスクリプトを貼っつけてください。
サーバー側
using System; using System.Collections.Generic; using System.Net; using System.Threading; using Cysharp.Threading.Tasks; using UnityEngine; using UnityEngine.InputSystem; namespace UDP { public class UdpTest : MonoBehaviour { [SerializeField] private string _sendData = "test"; [SerializeField] private List<string> address; [SerializeField] private int receivePort = 9000; [SerializeField] private int sendPort = 9001; private UDPServer _udp; private List<IPEndPoint> _ipEndPoints = new List<IPEndPoint>(); private void Start() { _udp = new UDPServer(receivePort); var token = this.GetCancellationTokenOnDestroy(); WaitReceive(token).Forget(); for (int i = 0; i < address.Count; i++) { _ipEndPoints.Add(new IPEndPoint(IPAddress.Parse(address[i]),sendPort)); } } private void Update() { if (Keyboard.current.digit0Key.wasPressedThisFrame) { _udp.Send(_sendData,_ipEndPoints[0]).Forget(); } if (Keyboard.current.digit1Key.wasPressedThisFrame) { _udp.Send(_sendData,_ipEndPoints[1]).Forget(); } if (Keyboard.current.digit2Key.wasPressedThisFrame) { _udp.Send(_sendData,_ipEndPoints[2]).Forget(); } } async UniTaskVoid WaitReceive(CancellationToken token) { while (!token.IsCancellationRequested) { string data = await _udp.Receive(token); Debug.Log(data); //await UniTask.Delay(TimeSpan.FromSeconds(3), cancellationToken: token); } } } }
インスペクターで送信先のESP32のIPアドレスを設定します。 このテスト用コードでは個別にキーを押して送信していますが、実際使用した際はfor文回して全ESP32に同じデータを送信していました。
クライアント側
#include"UDPLibrary.h" UDPLib _udp; char* ssid="ssid"; char* password="pass"; void setup() { Serial.begin(115200); WiFi.begin(ssid, password); while ( WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(500); } Serial.println("connected "+String(ssid)); Serial.println(WiFi.localIP()); _udp.begin("192.168.0.32",9000,9001); } void loop() { if(Serial.available()>0){ String text=Serial.readStringUntil('\n'); _udp.send(text); } String r=_udp.read(); if(r!=""){ Serial.println(r); } }
Arduino IDEのシリアルモニタでデータの確認できます。