반응형

컴퓨터에 연결된 조이스틱을 조사하고 각 버튼의 상태를 확인해 보자.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import pygame
 
pygame.init()
 
def main():
    clock = pygame.time.Clock()
 
    joysticks = {}
    # 조이스틱을 관리하기 위한 딕셔너리.
 
    done = False
    while not done:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True
 
            if event.type == pygame.JOYBUTTONDOWN:
                print("Joystick button pressed.")
                            
            if event.type == pygame.JOYBUTTONUP:
                print("Joystick button released.")
 
            if event.type == pygame.JOYAXISMOTION:
                print("Joystick axis motion detected.")
                
            if event.type == pygame.JOYDEVICEADDED:
                joy = pygame.joystick.Joystick(event.device_index)
                # 컴퓨터에 연결된 물리적 조이스틱에 접근하기 위한 Joystick 클래스 생성.
                # Joystick 클래스 생성자 인수에는 0 부터 pygame.joystick.get_count()-1 까지 대입한다.
                # event.device_index는 0부터 연결된 조이스틱의 숫자 -1 까지 증가하는거 같다.
                joysticks[joy.get_instance_id()] = joy
                # joysticks 딕셔너리에 새로 생성된 Joystick 클래스를 추가한다.
                # joy.get_instance_id() 는 joystick의 instance ID를 반환한다. 이 값은 Joystick 클래스
                # 생성자 인수로 넣은 값과 같은거 같다. event.instance_id 와 같은 값을 가진다.
                print(f"Joystick #{joy.get_instance_id()} connencted")
 
            if event.type == pygame.JOYDEVICEREMOVED:
                del joysticks[event.instance_id]
                # 조이스틱을 분리하면 joysticks 딕셔너리에서 분리된 조이스틱을 삭제한다.
                print(f"Joystick #{event.instance_id} disconnected")
 
        for joystick in joysticks.values():
        # dictionary.values() 는 dict_values라는 리스트 객체를 리턴하며 이 안에는 값의 목록만 들어 있다.
            jid = joystick.get_instance_id()
            name = joystick.get_name()
            guid = joystick.get_guid()
 
            axes = joystick.get_numaxes()
            for i in range(axes):
                axis = joystick.get_axis(i)
                if axis > 0.1 or axis < -0.1:
                    print(f"ID: {jid} Name: {name}, GUID: {guid}, Axis #{i}, Value: {axis:>6.3f}")
 
            buttons = joystick.get_numbuttons()
            for i in range(buttons):
                button = joystick.get_button(i)
                if button != 0:
                    print(f"ID: {jid} Name: {name}, GUID: {guid}, Button #{i:>2} Value: {button}")
                    
    clock.tick(30)
 
if __name__ == "__main__":
    main()
    pygame.quit()
 

 

조이스틱이 하나만 연결되어 있다면 조금 더 간단히 상태를 확인 할 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import pygame
 
pygame.init()
 
joy = None
jid = None
name = None
guid = None
 
def main():
    clock = pygame.time.Clock()
    done = False
 
    while not done:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True
 
            if event.type == pygame.JOYBUTTONDOWN:
                print("Joystick button pressed.")
                buttons = joy.get_numbuttons()
                for i in range(buttons):
                    button = joy.get_button(i)
                    if button != 0:
                        print(f"ID: {jid} Name: {name}, GUID: {guid}, Button #{i:>2} Value: {button}")
                            
            if event.type == pygame.JOYBUTTONUP:
                print("Joystick button released.")
 
            if event.type == pygame.JOYAXISMOTION:
                print("Joystick axis motion detected.")
                # 방향 버튼을 눌렀을 때 -1, 0, 1 같은 정수가 나오지 않을 수 도 있다.
                if joy.get_axis(0> 0.5:                    
                    print(f"ID: {jid} Name: {name}, GUID: {guid}, Axis: L/R Value: RIGHT")
                if joy.get_axis(0< -0.5:
                    print(f"ID: {jid} Name: {name}, GUID: {guid}, Axis: L/R, Value: LEFT")
                if joy.get_axis(1> 0.5:
                    print(f"ID: {jid} Name: {name}, GUID: {guid}, Axis: U/D, Value: DOWN")
                if joy.get_axis(1< -0.5:
                    print(f"ID: {jid} Name: {name}, GUID: {guid}, Axis: U/D, Value: UP")
 
            if event.type == pygame.JOYDEVICEADDED:
                joy = pygame.joystick.Joystick(event.device_index)
                jid = joy.get_instance_id()
                name = joy.get_name()
                guid = joy.get_guid()
                print(f"Joystick #{joy.get_instance_id()} connencted")
 
            if event.type == pygame.JOYDEVICEREMOVED:
                print(f"Joystick #{event.instance_id} disconnected")
 
    clock.tick(30)
 
if __name__ == "__main__":
    main()
    pygame.quit()
 

 

 

버튼을 누를때 마다 상태를 표시한다.

 

반응형
Posted by J-sean
:
반응형

2022.04.05 - [C#] - C# Joystick(Gamepad) Input Check - 조이스틱(게임패드) 입력 확인 1 의 내용을 참고하여 조금 더 간단히 조이스틱(게임패드) 입력을 확인해 보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
using System.Threading;
using System.Diagnostics;
 
using HidSharp;
using HidSharp.Utility;
using HidSharp.Reports;
 
namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            HidSharpDiagnostics.EnableTracing = true;
            HidSharpDiagnostics.PerformStrictChecks = true;
 
            DeviceList list = DeviceList.Local;
            list.Changed += (sender, e) => Console.WriteLine("Device list changed.");
 
            HidDevice[] hidDeviceList = list.GetHidDevices().ToArray();
 
            foreach (HidDevice dev in hidDeviceList)
            {
                if (!dev.ToString().Contains("gamepad"))
                {
                    Console.WriteLine(dev.GetProductName() + " is not a gamepad.");
                    continue;
                }
 
                try
                {
                    ReportDescriptor reportDescriptor = dev.GetReportDescriptor();
 
                    Debug.Assert(dev.GetMaxInputReportLength() == reportDescriptor.MaxInputReportLength);
                    Debug.Assert(dev.GetMaxOutputReportLength() == reportDescriptor.MaxOutputReportLength);
                    Debug.Assert(dev.GetMaxFeatureReportLength() == reportDescriptor.MaxFeatureReportLength);
 
                    foreach (DeviceItem deviceItem in reportDescriptor.DeviceItems)
                    {
                        {
                            HidStream hidStream;
                            if (dev.TryOpen(out hidStream))
                            {
                                Console.WriteLine(dev.GetProductName() + " opened.");
                                hidStream.ReadTimeout = Timeout.Infinite;
 
                                using (hidStream)
                                {
                                    byte[] inputReportBuffer = new byte[dev.GetMaxInputReportLength()];
                                    HidSharp.Reports.Input.HidDeviceInputReceiver inputReceiver =
                                        reportDescriptor.CreateHidDeviceInputReceiver();
                                    HidSharp.Reports.Input.DeviceItemInputParser inputParser =
                                        deviceItem.CreateDeviceItemInputParser();
 
                                    inputReceiver.Start(hidStream);
 
                                    while (true)
                                    {
                                        if (inputReceiver.WaitHandle.WaitOne(1000))
                                        {
                                            if (!inputReceiver.IsRunning) { break; } // Disconnected?
 
                                            Report report;
                                            while (inputReceiver.TryRead(inputReportBuffer, 0out report))
                                            {
                                                if (inputParser.TryParseReport(inputReportBuffer, 0, report) && inputParser.HasChanged)
                                                {
                                                    //inputParser.HasChanged는 버튼을 누르거나 놓는 등 상태가 변경되는 경우를 뜻한다.
 
                                                    //Console.WriteLine(BitConverter.ToString(inputReportBuffer));
 
                                                    if (inputReportBuffer[3== 0x00)
                                                    {
                                                        Console.WriteLine("Left");
                                                    }
                                                    else if (inputReportBuffer[3== 0xFF)
                                                    {
                                                        Console.WriteLine("Right");
                                                    }
                                                    else if (inputReportBuffer[4== 0x00)
                                                    {
                                                        Console.WriteLine("Up");
                                                    }
                                                    else if (inputReportBuffer[4== 0xFF)
                                                    {
                                                        Console.WriteLine("Down");
                                                    }
                                                    else if (inputReportBuffer[5== 0x1F)
                                                    {
                                                        Console.WriteLine("X");
                                                    }
                                                    else if (inputReportBuffer[5== 0x2F)
                                                    {
                                                        Console.WriteLine("A");
                                                    }
                                                    else if (inputReportBuffer[5== 0x4F)
                                                    {
                                                        Console.WriteLine("B");
                                                    }
                                                    else if (inputReportBuffer[5== 0x8F)
                                                    {
                                                        Console.WriteLine("Y");
                                                    }
                                                    else if (inputReportBuffer[6== 0x01)
                                                    {
                                                        Console.WriteLine("L Shoulder");
                                                    }
                                                    else if (inputReportBuffer[6== 0x02)
                                                    {
                                                        Console.WriteLine("R Shoulder");
                                                    }
                                                    else if (inputReportBuffer[6== 0x10)
                                                    {
                                                        Console.WriteLine("Select");
                                                    }
                                                    else if (inputReportBuffer[6== 0x20)
                                                    {
                                                        Console.WriteLine("Start");
                                                    }
                                                }
                                            }
                                        }
 
                                        if (Console.KeyAvailable)
                                        {
                                            ConsoleKeyInfo consoleKeyInfo = Console.ReadKey(true);
 
                                            if (consoleKeyInfo.Key == ConsoleKey.Escape)
                                            {
                                                Console.WriteLine("User stopped.");
                                                break;
                                                //hidStream.Close();
                                            }
                                        }
                                    }
                                }
 
                                Console.WriteLine("Gamepad closed.");
                            }
                            else
                            {
                                Console.WriteLine("Failed to open device.");
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
            }
        }
    }
}
 

 

소스를 입력하고 빌드한다.

 

 

프로그램을 실행하고 게임패드의 버튼을 누르면 어떤 버튼이 눌리는지 표시된다.

 

반응형
Posted by J-sean
:
반응형

컴퓨터에 연결된 모든 HID(Human Interface Devices)를 확인하고 조이스틱(게임패드)의 입력을 체크해 보자.

 

HidSharp를 설치한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
using System.Threading;
using System.Diagnostics;
 
using HidSharp;
using HidSharp.Utility;
using HidSharp.Reports;
 
namespace ConsoleApp1
{
    class Program
    {
        static void WriteDeviceItemInputParserResult(HidSharp.Reports.Input.DeviceItemInputParser parser)
        {
            while (parser.HasChanged)
            {
                int changedIndex = parser.GetNextChangedIndex();
                DataValue previousDataValue = parser.GetPreviousValue(changedIndex);
                DataValue dataValue = parser.GetValue(changedIndex);
 
                Console.WriteLine(string.Format("  {0}: {1} -> {2}", (Usage)dataValue.Usages.FirstOrDefault(),
                    previousDataValue.GetPhysicalValue(), dataValue.GetPhysicalValue()));
            }
        }
 
        static void Main(string[] args)
        {
            HidSharpDiagnostics.EnableTracing = true;
            HidSharpDiagnostics.PerformStrictChecks = true;
 
            DeviceList list = DeviceList.Local;
            list.Changed += (sender, e) => Console.WriteLine("Device list changed.");
 
            Device[] allDeviceList = list.GetAllDevices().ToArray();
            Console.WriteLine("■ All device list:");
            foreach (Device dev in allDeviceList)
            {
                Console.WriteLine("- Name: " + dev.ToString() + "\n" + "  Path: " + dev.DevicePath);
            }
 
            Console.WriteLine("-----------------------------------------------");
 
            Stopwatch stopwatch = Stopwatch.StartNew();
            HidDevice[] hidDeviceList = list.GetHidDevices().ToArray();
 
            Console.WriteLine("■ HID device list (took {0} ms to get {1} devices):",
                              stopwatch.ElapsedMilliseconds, hidDeviceList.Length);
 
            foreach (HidDevice dev in hidDeviceList)
            {
                Console.WriteLine("- Name: " + dev);
                Console.WriteLine("  Path: " + dev.DevicePath);
 
                try
                {
                    ReportDescriptor reportDescriptor = dev.GetReportDescriptor();
 
                    // Lengths should match.
                    Debug.Assert(dev.GetMaxInputReportLength() == reportDescriptor.MaxInputReportLength);
                    Debug.Assert(dev.GetMaxOutputReportLength() == reportDescriptor.MaxOutputReportLength);
                    Debug.Assert(dev.GetMaxFeatureReportLength() == reportDescriptor.MaxFeatureReportLength);
 
                    foreach (DeviceItem deviceItem in reportDescriptor.DeviceItems)
                    {
                        foreach (uint usage in deviceItem.Usages.GetAllValues())
                        {
                            Console.WriteLine(string.Format("  Usage: {0:X4} {1}", usage, (Usage)usage));
                        }
 
                        {
                            Console.WriteLine("Opening device for 20 seconds...");
 
                            HidStream hidStream;
                            if (dev.TryOpen(out hidStream))
                            {
                                Console.WriteLine("Opened device.");
                                hidStream.ReadTimeout = Timeout.Infinite;
 
                                using (hidStream)
                                {
                                    byte[] inputReportBuffer = new byte[dev.GetMaxInputReportLength()];
                                    HidSharp.Reports.Input.HidDeviceInputReceiver inputReceiver = 
                                        reportDescriptor.CreateHidDeviceInputReceiver();
                                    HidSharp.Reports.Input.DeviceItemInputParser inputParser = 
                                        deviceItem.CreateDeviceItemInputParser();
 
                                    inputReceiver.Start(hidStream);
 
                                    int startTime = Environment.TickCount;
                                    while (true)
                                    {
                                        if (inputReceiver.WaitHandle.WaitOne(1000))
                                        {
                                            if (!inputReceiver.IsRunning) { break; } // Disconnected?
 
                                            Report report;
                                            while (inputReceiver.TryRead(inputReportBuffer, 0out report))
                                            {
                                                // Parse the report if possible.
                                                // This will return false if (for example) the report applies to a different DeviceItem.
                                                if (inputParser.TryParseReport(inputReportBuffer, 0, report))
                                                {
                                                    WriteDeviceItemInputParserResult(inputParser);
                                                }
                                            }
                                        }
 
                                        uint elapsedTime = (uint)(Environment.TickCount - startTime);
                                        if (elapsedTime >= 20000) { break; } // Stay open for 20 seconds.
                                    }
 
                                }
 
                                Console.WriteLine("Closed device.");
                            }
                            else
                            {
                                Console.WriteLine("Failed to open device.");
                            }
 
                            Console.WriteLine("  ---------------------------------------------");
                        }
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
            }
        }
    }
}
 

 

소스를 입력하고 빌드한다.

 

 

프로그램을 실행하면 연결된 모든 디바이스와 HID가 표시되고 open 가능한 디바이스(usb gamepad)는 입력을 받아 표시한다.

포커스가 다른 프로그램에 있어도 디바이스의 입력을 확인 할 수 있다.

 

 

각 버튼이 눌렸을때 inputReportBuffer의 변화되는 내용을 확인해 보면 아래와 같다.

(첫 번째 바이트의 '01'은 Report ID인거 같다)

 

1
Console.WriteLine(BitConverter.ToString(inputReportBuffer));
 

 

01-80-80-7F-7F-0F-00-00
01-80-80-00-7F-0F-00-00
  GenericDesktopX: 127 -> 0

01-80-80-7F-7F-0F-00-00
01-80-80-FF-7F-0F-00-00
  GenericDesktopX: 127 -> 255

01-80-80-7F-7F-0F-00-00
01-80-80-7F-00-0F-00-00
  GenericDesktopY: 127 -> 0
  
01-80-80-7F-7F-0F-00-00
01-80-80-7F-FF-0F-00-00
  GenericDesktopY: 127 -> 255
※게임패드의 방향버튼은 기본이(버튼이 눌리지 않았을때) 127

01-80-80-7F-7F-0F-00-00
01-80-80-7F-7F-1F-00-00
  Button1: 0 -> 1

01-80-80-7F-7F-0F-00-00
01-80-80-7F-7F-2F-00-00
  Button2: 0 -> 1
  
01-80-80-7F-7F-0F-00-00
01-80-80-7F-7F-4F-00-00
  Button3: 0 -> 1

01-80-80-7F-7F-0F-00-00
01-80-80-7F-7F-8F-00-00
  Button4: 0 -> 1
※게임패드의 버튼은 기본이(버튼이 눌리지 않았을때) 0

 

※ HidSharp

 

반응형
Posted by J-sean
:
반응형

Bartop Arcade Machine을 만들기 위해서는 Joystick이 필요하다. 완제품으로 만들어져 컴퓨터에 연결만 하면 되는 조이스틱도 많지만 원하는 디자인으로 만들기 위해서는 조이스틱, 버튼, 케이블 등을 따로 구매해야 한다.


2020/03/17 - [Review] - DHU-4500 Joystick : 조이스틱

2020/11/12 - [Vita] - 오래된 노트북을 오락실 게임기로 만들어 보자.


Bartop Arcade Machine


2주 전 AliExpress에서 구매한 조이스틱이 도착 했다.


박스를 개봉하니 Push Button이 보인다.


버튼과 함께 다른 상자가 하나 더 들어 있다.



다른 상자에는 조이스틱과 Encoder(Zero Delay) 그리고 각종 케이블이 들어 있다.


각종 케이블들과 Encoder.




Zero Delay Encoder.



뒷면.


조이스틱.


Push Button. 왼쪽이 24mm, 오른쪽이 30mm 이다.


Encoder에 USB 케이블, 조이스틱, 버튼을 위와 같이 연결해 준다. 버튼은 필요한 만큼 추가로 연결 한다.


※ 오른쪽 컨넥터 4개 설명

Mode: Analog - Digital 변환

Turbo: 버튼을 누르면 연사

Auto: 버튼을 누르지 않아도 연사

Clear: Turbo/Auto 취소



10개의 버튼을 테스트 하기 위해 연결한 모습.


케이스 없이 이 조이스틱을 그대로 사용하긴 어렵다. 우선 테스트를 위해 버튼과 조이스틱 케이블을 모두 연결 하고 컴퓨터에 USB를 연결하면 특별한 드라이버나 설정 없이 바로 인식 된다. RetroPie등을 사용한다면 키설정을 다시 진행 해 준다.


반응형
Posted by J-sean
:
반응형

조이패드 스타일의 컨트롤러를 싫어하기도 하지만 대부분의 컴퓨터 게임은 키보드와 마우스로 컨트롤 하는게 편했고 굳이 조이스틱을 살 필요는 없었다. 하지만 최근 레트로 게임기 제작에 관심이 생기면서 조이스틱이 아니면 컨트롤이 힘든 게임들을 설치하게 되었고 어떤 제품이 있는지 알아 보기 시작했다.


꽤나 많은 제품들이 있었지만 모두 직접 사용해 볼 수는 없으므로 우선 가장 저렴한 제품을 사서 간단히 테스트 해 보기로 했다. 예상 외로 많이 검색 되었던 것은 완제품 조이스틱이 아닌 DIY가 가능한 조이스틱 부품들이었다.


2020/04/09 - [Review] - DIY Sanwa Arcade Joystick Kit - DIY 산와 아케이드 조이스틱 키트


케이스를 직접 만들고 조립해야 하는 조이스틱 부품들


언제가 될지는 모르지만 DIY 조이스틱도 만들어 보기 위해 우선 알리익스프레스에 주문해 두었다. 부품 자체는 저렴하지만 케이스 제작과 인건비를 모두 계산하면 왠만한 완제품을 사는게 쌀지도 모르겠다. 어쨌든 그래서 구입한 완제품 조이스틱, 다훈전자 DHU-4500.


전체 모습


스틱 1개, 버튼 8개, LED 3개 그리고 선택(연사)스위치가 있다.


뒷면



설명서


윈도우10을 사용한다면 드라이버 업데이트가 필요하므로 설명서를 잘 읽고 진행 해야 한다.














설명서를 다 읽었으면 드라이버 업데이트를 진행해 보자. 조이스틱을 컴퓨터에 연결 한다.


시스템 - 장치 관리자를 클릭 한다.


설명서에는 libusb-win32 devices → BETTER_USB_HS가 나온다고 되어 있으나 내 컴퓨터에는 Atmel USB Devices가 표시 되었다. 예전에 Atmel관련 드라이버를 설치했기 때문인거 같다. DHU-4500도 Atmel 칩을 사용하는 걸까? 어쨌든 마우스 오른쪽 클릭 후 드라이버 업데이트를 선택 한다.


컴퓨터에서 드라이버 소트프웨어 검색을 클릭 한다.


컴퓨터의 사용 가능한 드라이버 목록에서 직접 선택을 클릭하고 다음을 클릭한다.



USB 입력 장치를 선택하고 다음을 클릭 한다.


드라이버 업데이트가 완료 된다.


Windows 설정에서 컨트롤러를 검색한다. USB 게임 컨트롤러 설정이 표시 되면 선택 한다.


DAHOON USB Joystick을 선택하고 속성을 클릭한다.



스틱과 버튼을 테스트 하고 설정 할 수 있다


조이스틱이 연결되면 POWER LED가 켜진다.


스틱이 움직이거나 버튼이 눌리면 USE LED가 켜진다.


선택 스위치를 I에 놓고 버튼을 누르면 단발로 작동 하지만 II로 바꾸면 연사로 작동 되고 LOCK LED가 켜진다.



크기가 작아 손을 올려 놓기가 좀 불편하고 너무 가벼워 스트리트 파이터 같은 격투 게임을 플레이 하다 보면 빠른 움직임에 스틱과 함께 기기 자체가 움직여 버리는 등 단점도 있지만 다른 조이스틱에 비해 저렴한 가격으로 간단한 레트로 게임을 즐기기에는 크게 문제 되지 않는다. 


간단한 레트로 게임 플레이에 적당한 조이스틱.


반응형
Posted by J-sean
: