6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-07-31

SS 192.png 理解のためのメモその2

#やること

前回記事の続きです。

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用スクリプト

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;

//送るデータのサイズ。データはショート型の符号付き2バイト、送信時は1バイトに変換。
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]);//1バイトずつ送信
    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用スクリプト

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型変数(1バイト)
    static short[] r_short_buf = new short[MSG_SIZE];//データ格納用のshort型変数(2バイト)
    public int[] r_showdata = new int[10];//受信データインスペクタ表示用

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

    static long count = 0;//データ受信用のchar型変数(1バイト)
    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側の実行画面(インスペクター)

■ ESP32のシリアルモニタ(Arduino IDE)

#おわりに

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

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?