まめ畑

ゆるゆると書いていきます

WiiRemoteを使ってみた

以前、Wiiリモコンの重力センサーについて書きましたがC#Wiiリモコンを使用出来る、WiimoteLibを使ってみました。
このライブラリは1本のリモコンしか操作出来ないのですが、ソースが公開されているので複数本に対応させる事も可能です。
以下のプログラムは、PCとリモコンをペアリングさせてから実行して下さい。


先ずは簡単に加速度センサーの値を取得してみます。

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using WiimoteLib;

namespace WiiRemote
{
    class Program
    {
        static Wiimote wii;
        static Mutex mx;

        static void Main(string[] args)
        {
            try
            {
                wii = new Wiimote();
                mx = new Mutex();
        //イベントハンドラを登録
                wii.WiimoteChanged += new WiimoteChangedEventHandler(wii_WiimoteChanged);
                wii.Connect();   //リモコンにつなぐ
                wii.SetReportType(Wiimote.InputReport.ButtonsAccel, true);   //リモコンのイベント取得条件を設定
                wii.SetLEDs(true,false,false,true);   //LEDを設定してみる
            }
            catch (Exception ex) { }
        }

        static void wii_WiimoteChanged(object sender, WiimoteChangedEventArgs args)
        {
            mx.WaitOne();
            WiimoteState ws = args.WiimoteState;
            Console.WriteLine("X:{0} Y:{1} Z:{2}", ws.AccelState.X, ws.AccelState.Y, ws.AccelState.Z);
            mx.ReleaseMutex();
        }
    }
}

簡単ですね。
Mutexを使っているのは、同時にイベントが実行されないようにするためです。
SetReportTypeでどのような条件でイベントを呼び出すか設定できます。
このソースの設定だと、ボタンと加速度の変化で呼び出されます。
つまり、コンソールにガンガン流れてきます。
このほかにボタンの押下時のみや加速度センサーのみも設定可能です。
LEDをセットするにはTrueかFalseを入れるだけです。
左からリモコンのLEDに対応しています。


次はGUIを使ってみます。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using WiimoteLib;

namespace WiiTest
{
    public partial class Form1 : Form
    {
        private delegate void WiiStatChangeDelegate(WiimoteChangedEventArgs args);

        private Wiimote wii = null;
        private Mutex mx = null;
        private bool isRumble = true;
        private WiimoteState gStat;

        readonly private int rumbleInterval = 100;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            try
            {
                this.mx = new Mutex();
                this.wii = new Wiimote();
                this.wii.WiimoteChanged += new WiimoteChangedEventHandler(wii_WiimoteChanged);
                this.wii.Connect();
                this.wii.SetReportType(Wiimote.InputReport.ButtonsAccel, true);
                this.wii.SetLEDs(false, false, false, false);
            }
            catch (Exception ex)
            {
                MessageBox.Show( "エラーが発生したので起動出来ませんでした\n" + ex.Message,"起動エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Application.Exit();
            }
        }

        void wii_WiimoteChanged(object sender, WiimoteChangedEventArgs args)
        {
                BeginInvoke(new WiiStatChangeDelegate(GetWiiRemotStat), args);
        }

        private void GetWiiRemotStat(WiimoteChangedEventArgs args)
        {
            try
            {
                this.mx.WaitOne();
                WiimoteState wiiStat = args.WiimoteState;
                this.gStat = wiiStat;

                if (wiiStat.ButtonState.B)
                {
                    if (this.isRumble)
                    {
                        this.isRumble = true;
                        timer1.Stop();
                    }
                    this.wii.SetRumble(!wiiStat.Rumble);
                    Thread.Sleep(100);
                }

                if (wiiStat.ButtonState.A)
                {
                    this.wii.SetLEDs(!wiiStat.LEDState.LED1, !wiiStat.LEDState.LED2, !wiiStat.LEDState.LED3, !wiiStat.LEDState.LED4);
                    Thread.Sleep(100);
                }

                if (wiiStat.ButtonState.Plus)
                {
                    this.isRumble = false;
                    if (timer1.Interval - this.rumbleInterval <= 0)
                    {
                        timer1.Interval = 20;
                    }
                    else
                    {
                        timer1.Interval = timer1.Interval - 100;
                    }
                    Thread.Sleep(100);
                }

                if (wiiStat.ButtonState.Minus)
                {
                    this.isRumble = false;
                    timer1.Interval = timer1.Interval + this.rumbleInterval;
                    Thread.Sleep(100);
                }

                if (wiiStat.ButtonState.Home)
                {
                    timer1.Stop();
                    this.wii.SetRumble(false);
                    this.wii.SetLEDs(false,false,false,false);
                }

                if (!this.isRumble)
                {
                    timer1.Start();
                    this.wii.SetRumble(false);
                    this.isRumble = true;
                }

                this.progressBar1.Value = (wiiStat.Battery > 0xc8 ? 0xc8 : (int)wiiStat.Battery);
                float f = (((100.0f * 48.0f * (float)(wiiStat.Battery / 48.0f))) / 192.0f);
                label2.Text = f.ToString("F");
                notifyIcon1.Text = label2.Text + "%";
                this.mx.ReleaseMutex();

            }catch(Exception ex){}
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.wii.Disconnect();
        }

        private void Form1_Resize(object sender, EventArgs e)
        {
            if (this.WindowState == FormWindowState.Minimized)
            {
                this.Visible = false;
                this.ShowInTaskbar = false;
            }
        }

        private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            this.Visible = true;
            this.ShowInTaskbar = true;
            this.WindowState = FormWindowState.Normal;
            this.Activate();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            this.wii.SetRumble(!this.gStat.Rumble);
        }
    }
}

このソースはリモコンの+で振動を遅く、マイナスで早く、AボタンでLEDを点灯します。
また、GUI上にバッテリー残量を表示します。
振動はスタートとストップなのでその間隔を変化させてみました。


ソースを呼んでもらえればわかりますが、各ボタンのイベントかどうかを判断するのは簡単ですね。
また、生データからバッテリー残量の%表示の方法もソース中の式で算出出来ます。
注意する事というか、当然なのですがイベントハンドラ内でGUIの内容を変化させる場合はInvokeする必要があります。


他にも色々なフィールドなどがあるのでドキュメントを読んで下さい。
IRセンサーやヌンチャクの情報なども取得出来ます。
クラコンなどを挿したりするとイベントが発生します。
しかし、純粋にリモコンと通信をしたいな。
このライブラリでは音がならせないので鳴らしたいな。

結論

リモコンすげーな