Membuat Virtual Joystik C#

Project kali ini adalah membuat sebuah aplikasi pada PC yang berperan menjembatani sebuah controller game yang mempunyai interface serial dengan game di PC. Controller game yang dimaksud adalah sebuah accelerometer (post sebelumnya).  Library yang digunakan adalah vjoy atau Virtual joystick. Library-nya bisa di download di link berikut.

vjoystick.sourceforge.net/site/index.php/dev

Compiler menggunakan Visual C# Express 2010.

Pada post sebelumnya saya merancang program GUI dengan chart untuk mengecek data yang diterima. Berikut tampilannya.

vjoy1

berikut kode nya.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;
using System.Configuration;

namespace serial
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //—menset event handler untuk DataReceived event—
            serialPort.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(DataReceived);
            //—menampilkan nama serialport yang tersedia pada
            // komputer—
            string[] portNames = System.IO.Ports.SerialPort.GetPortNames();
            for (int i = 0; i <= portNames.Length - 1; i++)
            {
                cbbCOMPorts.Items.Add(portNames[i]);
            }
            btnDisconnect.Enabled = false;
            horison = pictureBox1.Image;

            chart1.ChartAreas["ChartArea1"].AxisX.Minimum = 0.0;
            chart1.ChartAreas["ChartArea1"].AxisX.Maximum = 200.0;
            chart1.ChartAreas["ChartArea1"].AxisY.Minimum = -60.0;
            chart1.ChartAreas["ChartArea1"].AxisY.Maximum = 60.0;

            chart1.Series["Series1"].Color = Color.Cyan;
            chart1.Series["Series2"].Color = Color.Red;
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            //—menutup akses serialport apabila akses serialport terbuka—
            if (serialPort.IsOpen)
            {
                serialPort.Close();
            }
            try
            {
                //—mengatur beberapa parameter untuk koneksi serial
                // port—
                serialPort.PortName = cbbCOMPorts.Text;
                serialPort.BaudRate = 9600;
                serialPort.Parity = System.IO.Ports.Parity.None;
                serialPort.DataBits = 8;
                serialPort.StopBits = System.IO.Ports.StopBits.One;
                //—buka serial port—
                serialPort.Open();
                //—menampilkan status dari serial port dan
                // enable/disable -kan tombol—
                lblMessage.Text = cbbCOMPorts.Text + " connected.";
                btnConnect.Enabled = false;
                btnDisconnect.Enabled = true;
                timer1.Enabled = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void btnDisconnect_Click(object sender, EventArgs e)
        {
            try
            {
                //—tutup serial port—
                serialPort.Close();
                //—menampilkan status dari serial port dan
                // enable/disable -kan tombol—
                lblMessage.Text = serialPort.PortName +" disconnected.";
                btnConnect.Enabled = true;
                btnDisconnect.Enabled = false;
                timer1.Enabled = false;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            try
            {
                //—menulis tipe data string pada serial port—
                serialPort.Write(txtDataToSend.Text + Environment.NewLine);
                //—menambahkan string yang telah dikirim pada TextBox control—
                txtDataReceived.AppendText(">" + txtDataToSend.Text +  Environment.NewLine);
                txtDataReceived.ScrollToCaret();
                //—bersihkanTextBox control—
                txtDataToSend.Text = string.Empty;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        string kata;
        string[] buff_kata = new string[8];
        //—Event handler untuk DataReceived event—
        private void DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            try
            {
                kata = serialPort.ReadLine();
                buff_kata = SplitWords(kata);
                if (kata != string.Empty)
                {
                    //txtDataReceived.Text = kata;
                    txtDataReceived.BeginInvoke(new myDelegate(updateTextBox));
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        static string[] SplitWords(string s)
        {
            //
            // Split on all non-word characters.
            // ... Returns an array of all the words.
            //
            return Regex.Split(s, " ");
            // @      special verbatim string syntax
            // \W+    one or more non-word characters together
        }

        //—Delegate and subroutine untuk ditampilkan pada TextBox control—
        public delegate void myDelegate();

        double  Kk,
                Xk_topi,
                Pk,
                R = 0.1,
                Q = 0.002,
                Xk_topi_prev = 0,
                Pk_prev = 1,
                Pk_update,
                Xk_topi_update;
        int kalman_filter_ku(double Zk)
        {
            //Time Update
            Xk_topi_update = Xk_topi_prev;
            Pk_update = Pk_prev + Q;
            //Measurement Update
            Kk = Pk_update / (Pk_update + R);
            Xk_topi = Xk_topi_update + (Kk * (Zk - Xk_topi_update));
            Pk = (1 - Kk) * Pk_update;
            Xk_topi_prev = Xk_topi;
            Pk_prev = Pk;
            return ((int)Xk_topi);
        }

        Image horison;
        int data_y,
            kalman_y;
        public void updateTextBox()
        {
            try
            {
                data_y = Convert.ToInt32(buff_kata[2]);
            }
            catch (Exception a)
            {

            }
            kalman_y = kalman_filter_ku((double)data_y);
            txtDataReceived.Text = "ACC:"+data_y.ToString()+" Kalman:"+kalman_y.ToString();
        }

        public static Image RotateImage(Image img, float rotationAngle)
        {
            //create an empty Bitmap image
            Bitmap bmp = new Bitmap(img.Width, img.Height);

            //turn the Bitmap into a Graphics object
            Graphics gfx = Graphics.FromImage(bmp);

            //now we set the rotation point to the center of our image
            gfx.TranslateTransform((float)bmp.Width / 2, (float)bmp.Height / 2);

            //now rotate the image
            gfx.RotateTransform(rotationAngle);

            gfx.TranslateTransform(-(float)bmp.Width / 2, -(float)bmp.Height / 2);

            //set the InterpolationMode to HighQualityBicubic so to ensure a high
            //quality image once it is transformed to the specified size
            gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;

            //now draw our new image onto the graphics object
            gfx.DrawImage(img, new Point(0, 0));

            //dispose of our Graphics object
            gfx.Dispose();

            //return the image
            return bmp;
        }

        int time,
            time_prev,
            amp,
            amp_offset = 0,
            amp_prev = 220,
            volt_div = 1,
            end_scope = 200,
            amp_kalman,
            amp_prev_kalman;
        private void timer1_Tick(object sender, EventArgs e)
        {
            pictureBox1.Image = RotateImage(horison, data_y);
            pictureBox2.Image = RotateImage(horison, kalman_y);
            time++;
            time_prev = time - 1;

            amp = (data_y * volt_div) + amp_offset;
            chart1.Series["Series1"].Points.AddXY(time, amp);
            amp_kalman = (kalman_y * volt_div) + amp_offset;
            chart1.Series["Series2"].Points.AddXY(time, amp_kalman);

            amp_prev = amp;
            amp_prev_kalman = amp_kalman;

            if (time > end_scope)
            {
                chart1.Series["Series1"].Points.Clear();
                chart1.Series["Series2"].Points.Clear();
                time = 0;
            }
        }
    }
}

Sedangkan untuk pengaplikasian controler game-nya saya disain lebih sederhana dengan tampilan sebagai berikut.
vjoy2
berikut kode nya.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using vJoyInterfaceWrap;
namespace serial
{
    public partial class Form1 : Form
    {
        // Declaring one joystick (Device id 1) and a position structure.
        static public vJoy joystick;
        static public vJoy.JoystickState iReport;
        static public uint id = 1;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //—menset event handler untuk DataReceived event—
            serialPort.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(DataReceived);
            //—menampilkan nama serialport yang tersedia pada
            // komputer—
            string[] portNames = System.IO.Ports.SerialPort.GetPortNames();
            for (int i = 0; i <= portNames.Length - 1; i++)
            {
                cbbCOMPorts.Items.Add(portNames[i]);
            }
            btnDisconnect.Enabled = false;
            joystick = new vJoy();
            if (!joystick.AcquireVJD(id))
            {
                txtDataReceived.Text = "gagal";
            }
            else
            {
                txtDataReceived.Text = "berhasil";
                joystick.SetAxis(16384, id, HID_USAGES.HID_USAGE_X);   //min 16 center 16384 max 32767
                joystick.SetAxis(16384, id, HID_USAGES.HID_USAGE_Y);   //min 16 center 16384 max 32767
            }
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            //—menutup akses serialport apabila akses serialport terbuka—
            if (serialPort.IsOpen)
            {
                serialPort.Close();
            }
            try
            {
                //—mengatur beberapa parameter untuk koneksi serial
                // port—
                serialPort.PortName = cbbCOMPorts.Text;
                serialPort.BaudRate = 9600;
                serialPort.Parity = System.IO.Ports.Parity.None;
                serialPort.DataBits = 8;
                serialPort.StopBits = System.IO.Ports.StopBits.One;
                //—buka serial port—
                serialPort.Open();
                //—menampilkan status dari serial port dan
                // enable/disable -kan tombol—
                lblMessage.Text = cbbCOMPorts.Text + " connected.";
                btnConnect.Enabled = false;
                btnDisconnect.Enabled = true;
                timer2.Enabled = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void btnDisconnect_Click(object sender, EventArgs e)
        {
            try
            {
                //—tutup serial port—
                serialPort.Close();
                //—menampilkan status dari serial port dan
                // enable/disable -kan tombol—
                lblMessage.Text = serialPort.PortName +" disconnected.";
                btnConnect.Enabled = true;
                btnDisconnect.Enabled = false;
                timer2.Enabled = false;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            try
            {
                //—menulis tipe data string pada serial port—
                serialPort.Write(txtDataToSend.Text + Environment.NewLine);
                //—menambahkan string yang telah dikirim pada TextBox control—
                txtDataReceived.AppendText(">" + txtDataToSend.Text +  Environment.NewLine);
                txtDataReceived.ScrollToCaret();
                //—bersihkanTextBox control—
                txtDataToSend.Text = string.Empty;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        string kata;
        string[] buff_kata = new string[8];
        //—Event handler untuk DataReceived event—
        private void DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            try
            {
                kata = serialPort.ReadLine();
                buff_kata = SplitWords(kata);
                if (kata != string.Empty)
                {
                    //txtDataReceived.Text = kata;
                    txtDataReceived.BeginInvoke(new myDelegate(updateTextBox));
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        static string[] SplitWords(string s)
        {
            //
            // Split on all non-word characters.
            // ... Returns an array of all the words.
            //
            return Regex.Split(s, " ");
            // @      special verbatim string syntax
            // \W+    one or more non-word characters together
        }

        //—Delegate and subroutine untuk ditampilkan pada TextBox control—
        public delegate void myDelegate();

        public void updateTextBox()
        {
            //data_y = Convert.ToInt32(buff_kata[2]);
            //kalman_y = kalman_filter_ku((double)data_y);
            //txtDataReceived.Text = "ACC:"+data_y.ToString()+" Kalman:"+kalman_y.ToString();
            //txtDataReceived.Text = "x:"+buff_kata[1] + " y:"+buff_kata[2]+" ori"+kata;
        }
        double  Kk_x,
                Xk_topi_x,
                Pk_x,
                R_x = 0.1,
                Q_x = 0.0002,
                Xk_topi_prev_x = 0,
                Pk_prev_x = 1,
                Pk_update_x,
                Xk_topi_update_x;
        int kalman_filter_ku_x(double Zk_x)
        {
            //Time Update
            Xk_topi_update_x = Xk_topi_prev_x;
            Pk_update_x = Pk_prev_x + Q_x;
            //Measurement Update
            Kk_x = Pk_update_x / (Pk_update_x + R_x);
            Xk_topi_x = Xk_topi_update_x + (Kk_x * (Zk_x - Xk_topi_update_x));
            Pk_x = (1 - Kk_x) * Pk_update_x;
            Xk_topi_prev_x = Xk_topi_x;
            Pk_prev_x = Pk_x;
            return ((int)Xk_topi_x);
        }

        double  Kk_y,
                Xk_topi_y,
                Pk_y,
                R_y = 0.1,
                Q_y = 0.0002,
                Xk_topi_prev_y = 0,
                Pk_prev_y = 1,
                Pk_update_y,
                Xk_topi_update_y;
        int kalman_filter_ku_y(double Zk_y)
        {
            //Time Update
            Xk_topi_update_y = Xk_topi_prev_y;
            Pk_update_y = Pk_prev_y + Q_y;
            //Measurement Update
            Kk_y = Pk_update_y / (Pk_update_y + R_y);
            Xk_topi_y = Xk_topi_update_y + (Kk_y * (Zk_y - Xk_topi_update_y));
            Pk_y = (1 - Kk_y) * Pk_update_y;
            Xk_topi_prev_y = Xk_topi_y;
            Pk_prev_y = Pk_y;
            return ((int)Xk_topi_y);
        }

        int val_z_prev;
        private void timer2_Tick(object sender, EventArgs e)
        {
            int val_x,
                val_y,
                kalman_x,
                kalman_y,
                buff_val_x =0,
                buff_val_y =0,
                val_z =0;
            try
            {
                buff_val_y = Convert.ToInt32(buff_kata[1]);
                buff_val_x = Convert.ToInt32(buff_kata[2]);
                val_z = Convert.ToInt32(buff_kata[3]);
                //txtDataReceived.Text = buff_val_x.ToString() + " " + buff_val_y.ToString();
            }
            catch (Exception a)
            {

            }

            val_x = (buff_val_x * 410) + 16384;
            val_y = (buff_val_y * 410) + 16384;

            kalman_x = kalman_filter_ku_x((double)val_x);
            kalman_y = kalman_filter_ku_y((double)val_y);

            if      ((val_z_prev > 80) && (val_z < -30))//kotak
            {
                //txtDataReceived.AppendText("===============kotak"+Environment.NewLine);
                joystick.SetBtn(true, id, 1);
            }
            else if ((val_z_prev < -30) && (val_z > 80))//eek
            {
                //txtDataReceived.AppendText("++++++++++++++++eek" + Environment.NewLine);
                joystick.SetBtn(true, id, 2);
            }
            else
            {
                //txtDataReceived.AppendText("none" + Environment.NewLine);
                joystick.SetBtn(false, id, 1);
                joystick.SetBtn(false, id, 2);
            }

            joystick.SetAxis(kalman_x, id, HID_USAGES.HID_USAGE_X);   //min 16 center 16384 max 32767
            joystick.SetAxis(kalman_y, id, HID_USAGES.HID_USAGE_Y);   //min 16 center 16384 max 32767
            val_z_prev = val_z;
        }
    }
}

Jangan lupa untuk memasukan library vjoynya.

vjoy3
Berikut hasilnya.

Berikut file project yang bisa di download.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s