Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Convert to a package and add support for the Classic Controller #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 18 commits into from
Closed

Convert to a package and add support for the Classic Controller #17

wants to merge 18 commits into from

Conversation

jfurcean
Copy link
Contributor

  • Converts the adafruit_nunchuck to a package to make it easier to incorporate support for additional Nunchuk extension controllers
  • adds addtional support for the wii classic controller
  • optimizes the read_data function for better response when reading from multiple sensors/buttons on the device

@caternuson
Copy link
Collaborator

Just to be sure, this is for the controller that plugged into the Wiimote like this?
https://www.nintendo.com/consumer/systems/wii/en_na/images/system/ClassicControllerUse.gif

What's the motivation for the update to read_data?

@jfurcean
Copy link
Contributor Author

Yes, it should work with the SNES ones as well. https://wiibrew.org/wiki/Wiimote/Extension_Controllers#List_of_extension_controllers

It is extremely slow if someone tries to read every input because the _read_register() function has three separate calls to time.sleep(). If the time since you last read isn't that long it is much more responsive to read from the buffer. This speeds up reading the inputs from the standard nunchuk as well.

    def _read_register(self, address):
        with self.i2c_device as i2c:
            time.sleep(_I2C_READ_DELAY)
            i2c.write(address)
            time.sleep(_I2C_READ_DELAY)
            i2c.readinto(self.buffer)
        time.sleep(_I2C_READ_DELAY)
        return self.buffer

Add support for uDraw GameTablet as a module
@caternuson
Copy link
Collaborator

As another approach, how about a new function, or property, called something like values that would do a single I2C transaction and return all the values in a tuple. All of the current byte extraction, etc. code for the various parameters could be moved there.

Using it would look something like:

jx, jy, C, Z, ax, ay, az = nunchuk.values

Then the existing properties could just become "convenience" methods to return only specific values of interest. Ex:

    @property
    def button_C(self):
        """Return current pressed state of button C."""
        return self.values[2]

They would still be as slow as before. For fastest user code, one would use values.

@jfurcean
Copy link
Contributor Author

Using your example though, this wouldn't force a read from the register if the user just wanted the button_C value. This would also require a very long I2C read function that would return a very different tuple for each type of device.

@dglaude
Copy link

dglaude commented Jan 17, 2021

Yes it works with the SNES mini controller. It is likely also working with the NES mini controller, but I don't have one to test. I posted collection and I can retest whenever needed: https://twitter.com/DavidGlaude/status/1350765991127638016?s=20

I am fine to adapting the uDraw code to other pattern, my first unpublished version was working like current Nunchuck, my second version is attached to this PR, ... if a third one is needed, then fine, but if we could avoid a fourth iteration. :-)

Maybe @caternuson can also guide us on documentation for package and how to make it more clear what is supported, because this PR is mostly about code and example.

About example, I have a mouse emulation for the uDraw GameTablet (and I impressed myself how this make the tablet useful). Since the Nunchuk mouse example are also in this repo, I guess it is fine to add this here.

@caternuson
Copy link
Collaborator

caternuson commented Jan 17, 2021

Using your example though, this wouldn't force a read from the register if the user just wanted the button_C value.

That would still happen. The call to values would do the register read. The [2] would return just the C button value, the rest would be thrown away.

This would also require a very long I2C read function that would return a very different tuple for each type of device.

The length of the I2C read would be no different. Everything is using the same 6 byte read. The problem with the current code is that this is done separately for each individual property. The values property would return everything from the single read. It's OK if this is different for each subclass.

@ladyada Thoughts on this being here vs. Community Bundle? This PR, and maybe others (uDraw?), will be adding hardware not currently in shop. I personally only have a Nunchuk for any testing.

@caternuson
Copy link
Collaborator

caternuson commented Jan 17, 2021

That all seems reasonable, except this approach would require someone to call the nunchuck.values before they could use nunchcuk.button_C

One could just call nunchcuk.button_C if they wanted. It would work exactly like current code. A full register read would be done and only the button C value would be returned. The button C code "calls" values for you.

@jfurcean
Copy link
Contributor Author

Sorry, I realized that and am re-writing it accordingly. Now should values return a tuple like (jx ,jy, C, Z, ax, ay, az) or ((jx, jy),(ax, ay, az),C,Z)

@caternuson
Copy link
Collaborator

Probably just a simple tuple. Python has named tuples, but I don't think CircuitPython supports them yet. By using tuple unpacking, the idea is that the code is self documenting w.r.t. to the return values:

jx, jy, C, Z, ax, ay, az = nunchuk.values

And one can use _ as a placeholder for values that are not of interest:

# get button C and accelo
_, _, C, _, ax, ay, az = nunchuk.values

Or could do this:

values = nunchuk.values
print("acceleration = {}, {}, {}".format(values[4], values[5], values[6]))
if values[2]:
    print("Button C pressed")

A dictionary would help make that code look a little better since you could access with something like values['button_C'], but then you couldn't use unpacking.

@jfurcean
Copy link
Contributor Author

I just updated it to use the approach suggested by @caternuson. @dglaude, I updated the uDraw as well, but I cannot test it. Can you please take a look and make any changes as needed?

@dglaude
Copy link

dglaude commented Jan 17, 2021

@caternuson the uDraw is already build into this PR, I am making PR to the upstream and they get accepted. :-)

@jfurcean I just checked your new version of udraw library using the udraw_simpletest and it work perfectly.

Now I will work on perfection and adapting mouse emulation for udraw, that would be udraw_mouse.py

@dglaude
Copy link

dglaude commented Jan 17, 2021

Should some or all of the *_simpletest use the new tuple method?
I plan my udraw_mouse.py to use that, maybe that is enough.
But I tested an alternate version of udraw_simpletest.py if needed.

jfurcean and others added 2 commits January 17, 2021 16:53
My local pylint complain... I hope the CI version will be OK.
@dglaude
Copy link

dglaude commented Jan 18, 2021

Since the beginning I was under the idea that I was testing with the "Classic Controller", but what I have is the "Classic Controller Pro". The difference is in the shoulder button that are digital. @jfurcean witch one are you using, maybe I need to dig the detail, I don't know if they are distinguisable or if something special can be added.

@jfurcean
Copy link
Contributor Author

@dglaude like you said they are basically identical except for the fact that the RT and LT can only be 0 or 31 on the Pro. They can be 0-31 on the standard, but the data layout is the same.

https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller

LX,LY are the left Analog Stick X and Y (0-63), RX and RY are the right Analog Stick X and Y (0-31), and LT and RT are the Left and Right Triggers (0-31). The left Analog Stick has twice the precision of the other analog values.

https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro

LX,LY are the left Analog Stick X and Y (0-63), RX and RY are the right Analog Stick X and Y (0-31), and LT and RT are the Left and Right Buttons (0 OR 31). The left Analog Stick has twice the precision of the other analog values.

@jfurcean
Copy link
Contributor Author

@caternuson @ladyada does it make more sense for this to be in the Community Bundle instead? I am hoping that there could eventually be support for other Wii accessories like the WiiChuck library has for Arduino.

@caternuson
Copy link
Collaborator

The _I2C_BUFFER_UPDATE_DELAY approach also introduces that delay for all reads. While it may improve the slowness issue for reading multiple values, it will have adverse effects if reading a single value.

This PR also has a lot going on. It would be better to break this up into multiple smaller PR's focused on one issue at a time. I'd suggest we pause this PR and start over focusing on the slowness issue to begin with. And open a new issue that explains that and provides the simplest possible code example to demonstrate the issue.

@jfurcean
Copy link
Contributor Author

Sounds good.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants