diff --git a/adafruit_nunchuk.py b/adafruit_nunchuk/__init__.py old mode 100644 new mode 100755 similarity index 55% rename from adafruit_nunchuk.py rename to adafruit_nunchuk/__init__.py index a3b0681..30bf78b --- a/adafruit_nunchuk.py +++ b/adafruit_nunchuk/__init__.py @@ -1,15 +1,12 @@ # SPDX-FileCopyrightText: 2019 Carter Nelson for Adafruit Industries # # SPDX-License-Identifier: MIT - """ `adafruit_nunchuk` ================================================================================ +Base Library for the Nintento Nunchuck Extension Controller libraries. -CircuitPython library for Nintendo Nunchuk controller - - -* Author(s): Carter Nelson +* Author(s): Carter Nelson, John Furcean Implementation Notes -------------------- @@ -29,15 +26,19 @@ from adafruit_bus_device.i2c_device import I2CDevice __version__ = "0.0.0-auto.0" -__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Nunchuk.git" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_PortalBase.git" _DEFAULT_ADDRESS = 0x52 _I2C_INIT_DELAY = 0.1 _I2C_READ_DELAY = 0.01 +_I2C_BUFFER_UPDATE_DELAY = 0.05 -class Nunchuk: - """Class which provides interface to Nintendo Nunchuk controller.""" +class NunchukBase: + """Base Class which provides interface to Nintendo Nunchuk style controllers. + :param i2c: An i2c device. + :address: an i2c address. Defaults to _DEFAULT_ADDRESS (0x52). + """ def __init__(self, i2c, address=_DEFAULT_ADDRESS): self.buffer = bytearray(6) @@ -49,37 +50,15 @@ def __init__(self, i2c, address=_DEFAULT_ADDRESS): i2c_dev.write(b"\xF0\x55") time.sleep(_I2C_INIT_DELAY) i2c_dev.write(b"\xFB\x00") + time.sleep(_I2C_INIT_DELAY) - @property - def joystick(self): - """Return tuple of current joystick position.""" - self._read_data() - return self.buffer[0], self.buffer[1] - - @property - def button_C(self): # pylint: disable=invalid-name - """Return current pressed state of button C.""" - return not bool(self._read_data()[5] & 0x02) - - @property - def button_Z(self): # pylint: disable=invalid-name - """Return current pressed state of button Z.""" - return not bool(self._read_data()[5] & 0x01) - - @property - def acceleration(self): - """Return 3 tuple of accelerometer reading.""" - self._read_data() - x = (self.buffer[5] & 0xC0) >> 6 - x |= self.buffer[2] << 2 - y = (self.buffer[5] & 0x30) >> 4 - y |= self.buffer[3] << 2 - z = (self.buffer[5] & 0x0C) >> 2 - z |= self.buffer[4] << 2 - return x, y, z + self.last_updated = 0 - def _read_data(self): - return self._read_register(b"\x00") + def read_data(self): + """Reads data stream from register""" + if (time.monotonic() - self.last_updated) > _I2C_BUFFER_UPDATE_DELAY: + self.last_updated = time.monotonic() + self._read_register(b"\x00") def _read_register(self, address): with self.i2c_device as i2c: diff --git a/adafruit_nunchuk/classic_controller.py b/adafruit_nunchuk/classic_controller.py new file mode 100755 index 0000000..55c543f --- /dev/null +++ b/adafruit_nunchuk/classic_controller.py @@ -0,0 +1,196 @@ +# SPDX-FileCopyrightText: Copyright (c) 2021 John Furcean +# +# SPDX-License-Identifier: MIT +""" +`adafruit_nunchuk.classic_controller` +================================================================================ +CircuitPython library for the Nintendo Wii Classic Controller + +* Author(s): John Furcean + +Implementation Notes +-------------------- + +**Hardware:** + +* Wii Classic Controller https://en.wikipedia.org/wiki/Classic_Controller + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://github.com/adafruit/circuitpython/releases +""" +from adafruit_nunchuk import NunchukBase + + +_DEFAULT_ADDRESS = 0x52 + + +class ClassicController(NunchukBase): + """Class which provides interface to the Nintendo Classic Controller.""" + + def __init__(self, i2c, address=_DEFAULT_ADDRESS): + super().__init__(i2c, address=address) + + @property + def values(self): # pylint: disable=too-many-locals + """returns tuple of values""" + + self.read_data() + + # https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller + + jrx = (self.buffer[0] & 0xC0) >> 3 + jrx |= (self.buffer[1] & 0xC0) >> 5 + jrx |= (self.buffer[2] & 0x80) >> 7 + jry = self.buffer[2] & 0x1F + + # left joystick + jlx = self.buffer[0] & 0x3F + jly = self.buffer[1] & 0x3F + + # analog trigger - left + atl = (self.buffer[2] & 0x60) >> 2 # pylint: disable=invalid-name + atl |= (self.buffer[3] & 0xE0) >> 5 # pylint: disable=invalid-name + + # analog trigger - right + atr = self.buffer[3] & 0x1F # pylint: disable=invalid-name + + # D-Pad + dl = not bool(self.buffer[5] & 0x2) # pylint: disable=invalid-name + dr = not bool(self.buffer[4] & 0x80) # pylint: disable=invalid-name + du = not bool(self.buffer[5] & 0x1) # pylint: disable=invalid-name + dd = not bool(self.buffer[4] & 0x40) # pylint: disable=invalid-name + + # Buttons + A = not bool(self.buffer[5] & 0x10) # pylint: disable=invalid-name + B = not bool(self.buffer[5] & 0x40) # pylint: disable=invalid-name + X = not bool(self.buffer[5] & 0x8) # pylint: disable=invalid-name + btr = not bool(self.buffer[4] & 0x2) # pylint: disable=invalid-name + btl = not bool(self.buffer[4] & 0x20) # pylint: disable=invalid-name + Y = not bool(self.buffer[5] & 0x20) # pylint: disable=invalid-name + ZL = not bool(self.buffer[5] & 0x80) # pylint: disable=invalid-name + ZR = not bool(self.buffer[5] & 0x4) # pylint: disable=invalid-name + start = not bool(self.buffer[4] & 0x4) + select = not bool(self.buffer[4] & 0x10) + home = not bool(self.buffer[4] & 0x8) + + return ( + jrx, + jry, + jlx, + jly, + dl, + dr, + du, + dd, + A, + B, + X, + Y, + atr, + atl, + btr, + btl, + ZR, + ZL, + start, + select, + home, + ) + + @property + def joystick_R(self): # pylint: disable=invalid-name + """Return tuple of current right joystick position.""" + + return self.values[0], self.values[1] + + @property + def joystick_L(self): # pylint: disable=invalid-name + """Return tuple of current left joystick position.""" + return self.values[2], self.values[3] + + @property + def dpad_L(self): # pylint: disable=invalid-name + """Return current pressed state of D-pad left.""" + return self.values[4] + + @property + def dpad_R(self): # pylint: disable=invalid-name + """Return current pressed state of D-pad right""" + return self.values[5] + + @property + def dpad_U(self): # pylint: disable=invalid-name + """Return current pressed state of D-pad up.""" + return self.values[6] + + @property + def dpad_D(self): # pylint: disable=invalid-name + """Return current pressed state of D-pad down""" + return self.values[7] + + @property + def button_A(self): # pylint: disable=invalid-name + """Return current pressed state of the A button""" + return self.values[8] + + @property + def button_B(self): # pylint: disable=invalid-name + """Return current pressed state of the B button""" + return self.values[9] + + @property + def button_X(self): # pylint: disable=invalid-name + """Return current pressed state of the X button""" + return self.values[10] + + @property + def button_Y(self): # pylint: disable=invalid-name + """Return current pressed state of the Y button""" + return self.values[11] + + @property + def button_RT(self): # pylint: disable=invalid-name + """Return current pressed state of the right trigger button""" + return self.values[14] + + @property + def button_LT(self): # pylint: disable=invalid-name + """Return current pressed state of the left trigger button""" + return self.values[15] + + @property + def button_ZR(self): # pylint: disable=invalid-name + """Return current pressed state of the Zr button""" + return self.values[16] + + @property + def button_ZL(self): # pylint: disable=invalid-name + """Return current pressed state of the Zl button""" + return self.values[17] + + @property + def button_start(self): + """Return current pressed state of the Start button""" + return self.values[18] + + @property + def button_select(self): + """Return current pressed state of the Select button""" + return self.values[19] + + @property + def button_home(self): + """Return current pressed state of the Home button""" + return self.values[20] + + @property + def button_plus(self): + """Return current pressed state of the Plus(Start) button""" + return self.button_start + + @property + def button_minus(self): + """Return current pressed state of the Minus(Select) button""" + return self.button_select diff --git a/adafruit_nunchuk/nunchuk.py b/adafruit_nunchuk/nunchuk.py new file mode 100755 index 0000000..9bc41e9 --- /dev/null +++ b/adafruit_nunchuk/nunchuk.py @@ -0,0 +1,83 @@ +# SPDX-FileCopyrightText: 2019 Carter Nelson for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" +`adafruit_nunchuk.nunchuck` +================================================================================ + +CircuitPython library for Nintendo Nunchuk controller + +* Author(s): Carter Nelson, John Furcean + +Implementation Notes +-------------------- + +**Hardware:** + +* `Wii Remote Nunchuk `_ +* `Wiichuck `_ + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://github.com/adafruit/circuitpython/releases +""" +from adafruit_nunchuk import NunchukBase + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Nunchuk.git" + +_DEFAULT_ADDRESS = 0x52 + + +class Nunchuk(NunchukBase): + """Class which provides interface to Nintendo Nunchuk controller.""" + + def __init__(self, i2c, address=_DEFAULT_ADDRESS): + super().__init__(i2c, address=address) + + @property + def values(self): + """Return tuple of values.""" + self.read_data() + + # https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Nunchuck + + # joystick + jx = self.buffer[0] # pylint: disable=invalid-name + jy = self.buffer[1] # pylint: disable=invalid-name + + # buttons + C = not bool(self.buffer[5] & 0x02) # pylint: disable=invalid-name + Z = not bool(self.buffer[5] & 0x01) # pylint: disable=invalid-name + + # acceleration + ax = (self.buffer[5] & 0xC0) >> 6 # pylint: disable=invalid-name + ax |= self.buffer[2] << 2 # pylint: disable=invalid-name + ay = (self.buffer[5] & 0x30) >> 4 # pylint: disable=invalid-name + ay |= self.buffer[3] << 2 # pylint: disable=invalid-name + az = (self.buffer[5] & 0x0C) >> 2 # pylint: disable=invalid-name + az |= self.buffer[4] << 2 # pylint: disable=invalid-name + + return jx, jy, C, Z, ax, ay, az + + @property + def joystick(self): + """Return tuple of current joystick position.""" + return self.values[0], self.values[1] + + @property + def button_C(self): # pylint: disable=invalid-name + """Return current pressed state of button C.""" + return self.values[2] + + @property + def button_Z(self): # pylint: disable=invalid-name + """Return current pressed state of button Z.""" + return self.values[3] + + @property + def acceleration(self): + """Return 3 tuple of accelerometer reading.""" + return self.values[4], self.values[5], self.values[6] diff --git a/adafruit_nunchuk/udraw.py b/adafruit_nunchuk/udraw.py new file mode 100755 index 0000000..ae5e136 --- /dev/null +++ b/adafruit_nunchuk/udraw.py @@ -0,0 +1,84 @@ +# SPDX-FileCopyrightText: Copyright (c) 2021 David Glaude +# +# SPDX-License-Identifier: MIT +""" +`udraw` +================================================================================ +CircuitPython library for the Nintendo Wii uDraw GameTablet + +* Author(s): David Glaude, John Furcean + +Implementation Notes +-------------------- + +**Hardware:** + +* Wii uDraw GameTablet http://wiibrew.org/wiki/Wiimote/Extension_Controllers/Drawsome_Tablet + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://github.com/adafruit/circuitpython/releases +* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice +`adafruit_nunchuk.classic_controller` +""" +from adafruit_nunchuk import NunchukBase + + +_DEFAULT_ADDRESS = 0x52 + + +class UDraw(NunchukBase): + """Class which provides interface to the uDraw GameTablet.""" + + def __init__(self, i2c, address=_DEFAULT_ADDRESS): + super().__init__(i2c, address=address) + + @property + def values(self): + """Return tuple of values.""" + + self.read_data() + + # http://wiibrew.org/wiki/Wiimote/Extension_Controllers/Drawsome_Tablet + + # position + px = (self.buffer[2] & 0x0F) << 8 # pylint: disable=invalid-name + px |= self.buffer[0] # pylint: disable=invalid-name + py = (self.buffer[2] & 0xF0) << 4 # pylint: disable=invalid-name + py |= self.buffer[1] # pylint: disable=invalid-name + + # pressure sensor reading + pressure = self.buffer[3] + + # buttons + pen = bool((self.buffer[5] & 0x04) >> 2) + C = not bool((self.buffer[5] & 0x02) >> 1) # pylint: disable=invalid-name + Z = not bool((self.buffer[5] & 0x01)) # pylint: disable=invalid-name + + return px, py, pen, C, Z, pressure + + @property + def position(self): + """Return tuple of current position.""" + return self.values[0], self.values[1] + + @property + def button_pen(self): + """Return current pressed state of the PEN button""" + return self.values[2] + + @property + def button_C(self): # pylint: disable=invalid-name + """Return current pressed state of the C button""" + return self.values[3] + + @property + def button_Z(self): # pylint: disable=invalid-name + """Return current pressed state of the Z button""" + return self.values[4] + + @property + def pressure(self): + """Return current pen pressure.""" + return self.values[5] diff --git a/docs/conf.py b/docs/conf.py index d9f48ad..d3eb015 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -25,7 +25,7 @@ # Uncomment the below if you use native CircuitPython modules such as # digitalio, micropython and busio. List the modules you use. Without it, the # autodoc module docs will fail to generate with a warning. -# autodoc_mock_imports = ["digitalio", "busio"] +autodoc_mock_imports = ["adafruit_bus_device"] intersphinx_mapping = { diff --git a/examples/classic_controller_simpletest.py b/examples/classic_controller_simpletest.py new file mode 100644 index 0000000..afcbab2 --- /dev/null +++ b/examples/classic_controller_simpletest.py @@ -0,0 +1,54 @@ +# SPDX-FileCopyrightText: Copyright (c) 2021 John Furcean +# +# SPDX-License-Identifier: MIT +import board +from adafruit_nunchuk.classic_controller import ClassicController + +controller = ClassicController(board.I2C()) +while True: + + # Right Joystick: (0-31,0-31), middle is (16,16) + if controller.joystick_R != (16, 16): + print(f"Right Joystick (x,y): {controller.joystick_R}") + + # Left Joystick: (0-63,063), middle is (32,32) + if controller.joystick_L != (32, 32): + print(f"Left Joystick (x,y): {controller.joystick_L}") + + # DPad: True or False + if controller.dpad_D: + print("D-Pad Down Pressed") + if controller.dpad_U: + print("D-Pad Up Pressed") + if controller.dpad_L: + print("D-Pad Left Pressed") + if controller.dpad_R: + print("D-Pad Right Pressed") + + # Buttons: True of False + if controller.button_ZR: + print("Button Pressed: ZR") + if controller.button_ZL: + print("Button Pressed: ZL") + if controller.button_A: + print("Button Pressed: A") + if controller.button_B: + print("Button Pressed: B") + if controller.button_X: + print("Button Pressed: X") + if controller.button_Y: + print("Button Pressed: Y") + if controller.button_RT: + print("Button Pressed: RT") + if controller.button_LT: + print("Button Pressed: LT") + if controller.button_home: + print("Button Pressed: Home") + if controller.button_start: + print("Button Pressed: Start") + if controller.button_select: + print("Button Pressed: Select") + if controller.button_plus: + print("Button Pressed: Plus") + if controller.button_minus: + print("Button Pressed: Minus") diff --git a/examples/nunchuk_accel_mouse.py b/examples/nunchuk_accel_mouse.py index 886bef8..7ee8c53 100644 --- a/examples/nunchuk_accel_mouse.py +++ b/examples/nunchuk_accel_mouse.py @@ -2,11 +2,12 @@ # SPDX-License-Identifier: MIT import board +import usb_hid from adafruit_hid.mouse import Mouse -import adafruit_nunchuk +from adafruit_nunchuk.nunchuk import Nunchuk -m = Mouse() -nc = adafruit_nunchuk.Nunchuk(board.I2C()) +m = Mouse(usb_hid.devices) +nc = Nunchuk(board.I2C()) centerX = 120 centerY = 110 diff --git a/examples/nunchuk_analog_mouse.py b/examples/nunchuk_analog_mouse.py index 05917da..3a5f28c 100644 --- a/examples/nunchuk_analog_mouse.py +++ b/examples/nunchuk_analog_mouse.py @@ -2,11 +2,12 @@ # SPDX-License-Identifier: MIT import board +import usb_hid from adafruit_hid.mouse import Mouse -import adafruit_nunchuk +from adafruit_nunchuk.nunchuk import Nunchuk -m = Mouse() -nc = adafruit_nunchuk.Nunchuk(board.I2C()) +m = Mouse(usb_hid.devices) +nc = Nunchuk(board.I2C()) centerX = 128 centerY = 128 diff --git a/examples/nunchuk_mouse.py b/examples/nunchuk_mouse.py index 7df4daa..433e366 100644 --- a/examples/nunchuk_mouse.py +++ b/examples/nunchuk_mouse.py @@ -2,13 +2,14 @@ # SPDX-License-Identifier: MIT import board +import usb_hid from adafruit_hid.mouse import Mouse -import adafruit_nunchuk +from adafruit_nunchuk.nunchuk import Nunchuk THRESHOLD = 10 -m = Mouse() -nc = adafruit_nunchuk.Nunchuk(board.I2C()) +m = Mouse(usb_hid.devices) +nc = Nunchuk(board.I2C()) while True: x, y = nc.joystick diff --git a/examples/nunchuk_simpletest.py b/examples/nunchuk_simpletest.py index db643fe..547a106 100644 --- a/examples/nunchuk_simpletest.py +++ b/examples/nunchuk_simpletest.py @@ -3,9 +3,9 @@ import time import board -import adafruit_nunchuk +from adafruit_nunchuk.nunchuk import Nunchuk -nc = adafruit_nunchuk.Nunchuk(board.I2C()) +nc = Nunchuk(board.I2C()) while True: x, y = nc.joystick diff --git a/examples/udraw_mouse.py b/examples/udraw_mouse.py new file mode 100644 index 0000000..5414b15 --- /dev/null +++ b/examples/udraw_mouse.py @@ -0,0 +1,55 @@ +# SPDX-FileCopyrightText: 2021 David Glaude +# +# SPDX-License-Identifier: MIT +import board +import usb_hid +from adafruit_hid.mouse import Mouse +from adafruit_nunchuk.udraw import UDraw + +udraw = UDraw(board.I2C()) + +m = Mouse(usb_hid.devices) + +zDown = False +pDown = False + +oldx = 4095 +oldy = 4095 + +while True: + + px, py, pen, C, Z, pressure = udraw.values + + P = pen or C # Both the C button and pen button work as LEFT mouse + + # Handeling the button to create mouse click + if P and not pDown: + m.press(Mouse.LEFT_BUTTON) + pDown = True + elif not P and pDown: + m.release(Mouse.LEFT_BUTTON) + pDown = False + + if Z and not zDown: + m.press(Mouse.RIGHT_BUTTON) + zDown = True + elif not Z and zDown: + m.release(Mouse.RIGHT_BUTTON) + zDown = False + + # Values (4095,4095) mean the pen is not near the tablet + if px == 4095 or py == 4095: + oldx = 4095 + oldy = 4095 + continue # We remember that the pen was raised UP + + # If we reach here, the pen was UP and is now DOWN + if oldx == 4095 or oldy == 4095: + oldx = px + oldy = py + continue + + if (px != oldx) or (py != oldy): # PEN has moved we move the HID mouse + m.move((px - oldx), (oldy - py), 0) + oldx = px + oldy = py diff --git a/examples/udraw_simpletest.py b/examples/udraw_simpletest.py new file mode 100644 index 0000000..07fa1a2 --- /dev/null +++ b/examples/udraw_simpletest.py @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: 2021 David Glaude +# +# SPDX-License-Identifier: MIT +import board +from adafruit_nunchuk.udraw import UDraw + +controller = UDraw(board.I2C()) + +while True: + + # Pressure: (8-248) + if controller.pressure != 8: + print("Pen pressure: ", controller.pressure) + + if controller.position != (4095, 4095): + print("Pen (x,y): ", controller.position) + + # Buttons: True of False + if controller.button_pen: + print("Button Pressed: Pen") + if controller.button_C: + print("Button Pressed: C") + if controller.button_Z: + print("Button Pressed: Z")