ESP32とUnityをWiFi送受信で連携する

![SS 192.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565043/a41cd967-f549-8677-1efa-2e99bf69518d.png) 理解のためのメモその２


#やること

[前回記事](https://qiita.com/Ninagawa_Izumi/items/b7c326c5a3d820decdda)の続きです。

ESP32とUnityをWiFiのアクセスポイント経由で連携します。
前回はESP32-UnityのUDPパケットを用いた一方向通信でしたが、
今回はそれに加え、送信・受信の両方をおこないます。

送受信するデータ形式は符号あり2バイトのShort型が配列で10個並んだものです。
これを1バイトずつ送り、ESP32,Unityそれぞれで受信後に2バイト数値に戻します。
ESP32からは一定間隔で送信、Unity側からは毎フレーム送信を行います。

Unity側ではインスペクターに受信データ、送信データを表示します。
ESP32側はシリアルモニタに送受信結果を表示します。

#ポイント
- ESP32側では2バイトを1バイトに変換するために共用体を利用
- Unity側では近い処理をBitConverterで実施
- Unity側の受信処理はスレッドを立てて実行

#参考
https://github.com/devemin/UDPesp32unity

今回の記事はほぼこちらのトレースであります。ありがたく勉強させていただきます。

#ESP32用スクリプト

```C++:Arduino
//ESP32 to Unity via UDP

#include <WiFi.h>
#include <WiFiUdp.h>

const char* ssid = "xxxxxxxxxx";//接続するアクセスポイントのSSID
const char* password = "xxxxxxxxxx";//上記のパスワード
const char* send_address = "192.168.xxx.xxx";  //送り先
const int send_port = 22222;  //送り先
const int receive_port = 22224;  //このESP32 のポート番号

WiFiUDP udp;

//送るデータのサイズ。データはショート型の符号付き２バイト、送信時は１バイトに変換。
static const int MSG_SIZE = 10;//送信データの数
static const int MSG_BUFF = MSG_SIZE * 2;//送信データのバイト数

//共用体の設定。共用体はたとえばデータをショートで格納し、バイト型として取り出せる
typedef union {
  short sval[MSG_SIZE];//ショート型
  uint8_t bval[MSG_BUFF];//符号なしバイト型
} UDPData;
UDPData s_upd_message_buf; //送信用共用体のインスタンスを宣言
UDPData r_upd_message_buf; //受信用共用体のインスタンスを宣言

int count = 0;//送信変数用のカウント

void setup() {
  Serial.begin(115200);
  delay(500);//シリアル準備待ち用ディレイ
  //WiFi 初期化
  WiFi.disconnect(true, true);//WiFi接続をリセット
  Serial.println("Connecting to WiFi to : " + String(ssid));//接続先を表示
  delay(100);
  WiFi.begin(ssid, password);//Wifiに接続
  while ( WiFi.status() != WL_CONNECTED) {//https://www.arduino.cc/en/Reference/WiFiStatus 返り値一覧
    delay(100);//接続が完了するまでループで待つ
  }
  Serial.println("WiFi connected.");//WiFi接続完了通知
  Serial.print("WiFi connected. ESP32's IP address is : ");
  Serial.println(WiFi.localIP());//デバイスのIPアドレスの表示

  //UDP 開始
  udp.begin(receive_port);
  delay(500);
}


//受信用の関数
//パケットが来ているか確認し、r_upd_message_buf配列に保存する
void receiveUDP() {
  int packetSize = udp.parsePacket();
  byte tmpbuf[MSG_BUFF];//パケットを一次受けする配列

  //データの受信
  Serial.print("[RESV] ");
  if (packetSize == MSG_BUFF) {//受信したパケットの量を確認
    udp.read(tmpbuf, MSG_BUFF);//パケットを受信
    for (int i = 0; i < MSG_BUFF; i++)
    {
      r_upd_message_buf.bval[i] = tmpbuf[i];//受信データを共用体に転記
    }
    for (int i = 0; i < MSG_SIZE; i++)
    {
      Serial.print(String(r_upd_message_buf.sval[i]));//シリアルモニタに出力
      Serial.print(", ");
    }
  } else
  {
  Serial.print("none.");//受信していない場合はシリアルモニタにnone.を出力
  }
  Serial.println();
}


//送信用の関数
void sendUDP() {
  String test = "";//表示用の変数

  udp.beginPacket(send_address, send_port);//UDPパケットの開始

  for (int i = 0; i < MSG_BUFF; i++) {
    udp.write(s_upd_message_buf.bval[i]);//１バイトずつ送信
    if (i % 2 == 0) {//表示用に送信データを共用体から2バイトずつ取得
      test += String(s_upd_message_buf.sval[i / 2]) + ", ";
    }
  }
  Serial.println("[SEND] " + test );//送信データ（short型）を表示

  udp.endPacket();//UDPパケットの終了
}


void loop() {

  //もしデータパケットが来ていれば受信する
  receiveUDP();

  //送信するデータを作成
  for (int i = 0; i < MSG_SIZE; i++) {
    s_upd_message_buf.sval[i] = (short)( i + count);
  }

  sendUDP();//UDPで送信

  count += 10;//データ作成用のカウントを追加
  if (count > 10000) {
    count = 0;
  }

  delay(500);
}

```

まずこちらのスケッチをESP32に書き込みます。
実行直後（リセット直後）に、シリアルモニタにESP32のIPアドレスを表示し、
その後は送信データを表示し続けます。


#Unity用スクリプト

```C#:udp_receive_test.cs
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System;

public class udp_send_receive_test : MonoBehaviour
{
    const string HOST = "192.168.xxx.xxx";//送信先(ESP32)のIPアドレス
    const int SEND_PORT = 22224;//送信用ポート設定
    const int RECEIVE_PORT = 22222;//受信に使うポートの番号
    static UdpClient udp_receive;//UDP受信を使う準備
    static UdpClient udp_send;//UDP送信を使う準備
    Thread thread;//スレッドを使う準備

    const int MSG_SIZE = 10;//受信するshort型データの個数
    static char[] r_char_buf = new char[MSG_SIZE * 2];//データ受信用のchar型変数（１バイト）
    static short[] r_short_buf = new short[MSG_SIZE];//データ格納用のshort型変数（２バイト）
    public int[] r_showdata = new int[10];//受信データインスペクタ表示用

    static byte[] s_char_buf = new byte[MSG_SIZE * 2];//データ送信用のchar型変数（１バイト）
    static short[] s_short_buf = new short[MSG_SIZE];//データ送信格納用のshort型変数（２バイト）
    public int[] s_showdata = new int[MSG_SIZE];//送信データインスペクタ表示用

    static long count = 0;//データ受信用のchar型変数（１バイト）
    string s_string;


    void Start()
    {
        udp_receive = new UdpClient(RECEIVE_PORT);
        udp_receive.Client.ReceiveTimeout = 1000;//UDP通信のタイムアウト設定 うまくいかない場合は0に？
        udp_send = new UdpClient();
        udp_send.Connect(HOST, SEND_PORT);
        thread = new Thread(new ThreadStart(ThreadMethod));//受信用スレッドを準備
        thread.Start();//受信用スレッドを開始
    }


    void Update()
    {
        //受信データをインスペクターに反映
        for (int i = 0; i < MSG_SIZE; i++)
        {
            r_showdata[i] = r_short_buf[i];//受信データをインスペクター表示用配列に転記
        }
        senddataUDP();//毎フレームデータを送信してみる
    }


    void OnApplicationQuit()//アプリ終了時の処理
    {
        udp_send.Close();//UDPの送信を終了
        thread.Abort();
    }

    private static void ThreadMethod()//受信スレッド用の関数
    {
        while (true)
        {
            IPEndPoint remoteEP = null;
            byte[] data = udp_receive.Receive(ref remoteEP);

            //パケットサイズをチェックし、受信データをUnityに反映
            if (data.Length == MSG_SIZE * 2) // 
            {
                for (int i = 0; i < MSG_SIZE; i++)
                {
                    r_short_buf[i] = BitConverter.ToInt16(data, i * 2);
                }
            }
        }
    }

    public void senddataUDP()
    {
        //送信するデータを作成
        for (int i = 0; i < MSG_SIZE; i++)
        {
            s_short_buf[i] = (short)(i + count);
        }

        //ショート型をバイトに変換
        for (int i = 0; i < MSG_SIZE; i++)
        {
            byte[] byteArray = BitConverter.GetBytes(s_short_buf[i]);
            s_char_buf[i * 2] = (byte)byteArray[0];
            s_char_buf[i * 2 + 1] = (byte)byteArray[1];
            s_showdata[i] = s_short_buf[i];//インスペクタ表示に送信データを反映
        }
        udp_send.Send(s_char_buf, MSG_SIZE * 2);

        count += 5;//データ作成用のカウントを追加
        if (count > 10000)
        {
            count = 0;
        }
    }
}

```

#使い方と実行
・Unityの新規プロジェクトを立ち上げます。
・アセットに新規スクリプトを作成してファイル名を「udp_send_receive_test」とし、上記のスクリプトをコピペします。
・ヒエラルキーウィンドウに空のオブジェクトを作成し、スクリプトをアタッチします。
・空のオブジェクトを選択し、インスペクターを表示します。
・スケッチが書き込まれたESP32を起動します。
・インスペクターにカウントアップする数値が表示されます。

#実行画面
■ Unity側の実行画面（インスペクター）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565043/f7f445de-4c0d-49a3-eaa6-1e0bc47f8bdb.png" width = 300>


■ ESP32のシリアルモニタ（Arduino IDE）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565043/95932969-a602-8dde-4e13-9e6bc7487a15.png" width = 500>



#おわりに

まだ変なところや無駄なところがあるかもしれません。
お気づきの点ございましたらご指摘いただければ幸いです。