aboutsummaryrefslogtreecommitdiff
path: root/src/pico/gamepad.py
blob: ddc533375885890ffa7270b97d23a385f2594dc3 (plain) (blame)
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
import struct
import time
import board
import digitalio
import analogio
from adafruit_hid import find_device

class Gamepad:
    def __init__(self, devices):
        self._gamepad_device = find_device(devices, usage_page=0x1, usage=0x05)

        self._buttons = {
            1: digitalio.DigitalInOut(board.GP0),
            2: digitalio.DigitalInOut(board.GP1),
            3: digitalio.DigitalInOut(board.GP2),
            4: digitalio.DigitalInOut(board.GP3),
            5: digitalio.DigitalInOut(board.GP4),
            6: digitalio.DigitalInOut(board.GP5),
            7: digitalio.DigitalInOut(board.GP6),
        }

        for button in self._buttons.values():
            button.direction = digitalio.Direction.INPUT
            button.pull = digitalio.Pull.UP

        self._joy_x_axis = analogio.AnalogIn(board.GP26)
        self._joy_y_axis = analogio.AnalogIn(board.GP27)

        self._report = bytearray(6)
        self._last_report = bytearray(6)

        self._buttons_state = 0
        self._joy_x = 0
        self._joy_y = 0
        self._joy_z = 0
        self._joy_r_z = 0

        try:
            self.reset_all()
        except OSError:
            time.sleep(1)
            self.reset_all()

    def read_buttons(self):
        self._buttons_state = 0
        for button_num, button in self._buttons.items():
            if not button.value:
                self._buttons_state |= 1 << (button_num - 1)

    def read_joysticks(self):
        self._joy_x = self._scale_joystick_value(self._joy_x_axis.value)
        self._joy_y = self._scale_joystick_value(self._joy_y_axis.value)

    def _scale_joystick_value(self, value):
        return (value - 32768) // 258

    def update(self):
        self.read_buttons()
        self.read_joysticks()
        self._send()

    def _send(self, always=False):
        struct.pack_into(
            "<Hbbbb",
            self._report,
            0,
            self._buttons_state,
            self._joy_x,
            self._joy_y,
            self._joy_z,
            self._joy_r_z,
        )

        if always or self._last_report != self._report:
            self._gamepad_device.send_report(self._report)
            self._last_report[:] = self._report

    def reset_all(self):
        self._buttons_state = 0
        self._joy_x = 0
        self._joy_y = 0
        self._joy_z = 0
        self._joy_r_z = 0
        self._send(always=True)

    @staticmethod
    def _validate_button_number(button):
        if not 1 <= button <= 16:
            raise ValueError("Button number must in range 1 to 16")
        return button

    @staticmethod
    def _validate_joystick_value(value):
        if not -127 <= value <= 127:
            raise ValueError("Joystick value must be in range -127 to 127")
        return value