見出し画像

【Unity】DMX512データの送信

舞台照明の通信規格として一般的に使われているDMXのデータをUnityから送ることで、これらの照明機器を制御してみたいと思います。

1. 準備

検証として以下の機材を用意しました。

DMX USBコントロールケーブルはFTDIのチップが載っており、ドライバをインストールしてシリアルポートとして認識すれば、別のものでも動作させることができる可能性があります。

PC側の環境としてはWindows10、Unity 2020.3.12f1で進めて行きます。

2. コード

DMXは電気的にはRS485の信号を利用したシリアル通信であるため、標準ライブラリのSerialPortクラスを使って制御することができます。

ただし、SerialPortクラスは.NET Standard 2.0には含まれないため、UnityのProject Settingsから「Api Compatibility Level」を.NET 4.xに変更する必要があります。

スクリーンショット 2021-06-30 180331

準備ができたら以下のスクリプトを適当なオブジェクトにアタッチします。その後、uGUIのスライダーをシーンに配置してインスペクタから参照を割り当ててください。
(※とにかく動かしたい人向け)

using UnityEngine;
using System.IO.Ports;
using System.Threading;
using UnityEngine.UI;

public class Controller : MonoBehaviour
{
   private SerialPort serial;
   byte[] dmxData = new byte[513];
   [SerializeField] Slider slider;

   void Start() {
       serial = new SerialPort("COM3", 250000);
       serial.DataBits = 8;
       serial.Parity = Parity.None;
       serial.StopBits = StopBits.Two;
       serial.Open();
   }

   private void SendDMX() {
       serial.BreakState = true;
       Thread.Sleep(1);
       serial.BreakState = false;
       Thread.Sleep(1);

       dmxData[1] = (byte) (slider.value * 255f);//ch1に0-255の値を設定
       serial.Write(dmxData, 0, dmxData.Length);
   }

   void Update() {
       SendDMX();
   }

   private void OnDestroy() {
       serial?.Close();
       serial?.Dispose();
   }
}

内容としてはSerialPortクラスを用意してUpdate関数でDMX信号を毎フレーム送信しています。今回使用したムービングヘッドの仕様なのかは分かりませんが、信号を単発で送るだけではなぜか動作せず、数回送ると問題なく動作することがわかりました。

また、DMXのソフトウェアツールであるQ Light Controller Plusを使用する場合でもデフォルトで30Hzの間隔で信号が送出されていることからも、常に一定の間隔で信号を送り続けるのが正しいのかもしれません。

上記のコードを(※とにかく動かしたい人向け)としたのは、簡略化のためにDMX信号の送出をメインスレッドでThread.Sleepでスレッドを止めて処理していたり、Thread.Sleepの精度がミリ秒オーダーであるためアバウトなDMX信号しか送れないというのが理由にあります。

そのため、場合によってはシーンかカクついたり、処理の重さや機器の相性によっては動かない場合もあります。

そこで、送信はメインスレッドとは異なる別スレッドで行い、遅延処理はマイクロ秒オーダーで処理できるようにしたスクリプトを以下に用意しました。スレッドの処理についてはUniRxを使っているので別途インストールして利用してください。

(※もう少しきっちりとしたい人向け)

using UnityEngine;
using System.IO.Ports;
using UnityEngine.UI;
using UniRx;
using System;

public class Controller1 : MonoBehaviour
{
   private SerialPort serial;
   byte[] dmxData = new byte[513];
   [SerializeField] Slider slider;

   void Start() {
       serial = new SerialPort("COM3", 250000);
       serial.DataBits = 8;
       serial.Parity = Parity.None;
       serial.StopBits = StopBits.Two;
       serial.Open();

       Observable
           .Interval(TimeSpan.FromSeconds(1/30f))
           .ObserveOn(Scheduler.ThreadPool)
           .Subscribe(_ => SendDMX())
           .AddTo(this);
   }

   private void SendDMX() {
       serial.BreakState = true;
       MicroSecDelay(176);// 176usec
       serial.BreakState = false;
       MicroSecDelay(16);// 16usec

       dmxData[1] = (byte) (slider.value * 255f);//ch1に0-255の値を設定
       serial.Write(dmxData, 0, dmxData.Length);
   }

   private void MicroSecDelay(int time) {
       var sw = new System.Diagnostics.Stopwatch();
       sw.Start();

       while (true) {
           sw.Stop();
           var elapsed = sw.ElapsedTicks * 1000 * 1000 / System.Diagnostics.Stopwatch.Frequency;
           if (elapsed > time) {
               break;
           } else {
               sw.Start();
           }
       }     
   }

   private void OnDestroy() {
       serial?.Close();
       serial?.Dispose();
   }
}

DMX信号送出の処理は以下のサイトを参考にさせていただきました。ありがとうございます!

3. 実行

DMX USBコントロールケーブルをPCとムービングヘッドに接続し、シリアルポートの番号を確認してスクリプト上のポート番号と一致させ実行します。

シーン上のスライダーを動かしてムービングヘッドが動けば成功です。今回の例では使用したムービングヘッドのch1がヘッドのパンに割り当てられているため、ヘッドが値によって変化することが確認できます。

4. おわりに

UnityからDMX信号を送る方法を調べても情報自体が少なかったり、動かないこともあったりして意外と苦労しました。FTDIのC#ラッパーの使用も検討しましたが、最終的に標準ライブラリのSerialPortクラスで動作させることができたのは良かったと思います。

USBタイプのDMXコントローラーについてはENTTECが有名ですが、もしかすると今回のコードでは動作しないかもしれません。もし手に入ったら試してみたいと思います。

またUnityからDMX信号を送る手段としては、Art-Netの方がやりやすい場合もありますので、利用する環境に応じて適切な機材や方法を選ぶのが良いでしょう。それでは良いDMXライフを!🌱

5. 参考


いいなと思ったら応援しよう!