pybricks / support

Pybricks support and general discussion
MIT License
109 stars 6 forks source link

[Feature] Gamepad controller investigation #1024

Open laurensvalk opened 1 year ago

laurensvalk commented 1 year ago

This is a spot for discussing gamepad connections, split off from https://github.com/pybricks/support/issues/262

laurensvalk commented 1 year ago
          > ### 4. BLE HID Peripherals

We may support connecting to generic HID devices over BLE (and possibly classic). Users could build on this to add specific mappings for certain devices like popular gaming consoles.

image

From https://github.com/pybricks/support/issues/191#issuecomment-1493256816 :

import binascii

import ubluetooth

from micropython import const

_IRQ_CENTRAL_CONNECT = const(1)
_IRQ_CENTRAL_DISCONNECT = const(2)
_IRQ_GATTS_WRITE = const(3)
_IRQ_GATTS_READ_REQUEST = const(4)
_IRQ_SCAN_RESULT = const(5)
_IRQ_SCAN_DONE = const(6)
_IRQ_PERIPHERAL_CONNECT = const(7)
_IRQ_PERIPHERAL_DISCONNECT = const(8)
_IRQ_GATTC_SERVICE_RESULT = const(9)
_IRQ_GATTC_SERVICE_DONE = const(10)
_IRQ_GATTC_CHARACTERISTIC_RESULT = const(11)
_IRQ_GATTC_CHARACTERISTIC_DONE = const(12)
_IRQ_GATTC_DESCRIPTOR_RESULT = const(13)
_IRQ_GATTC_DESCRIPTOR_DONE = const(14)
_IRQ_GATTC_READ_RESULT = const(15)
_IRQ_GATTC_READ_DONE = const(16)
_IRQ_GATTC_WRITE_DONE = const(17)
_IRQ_GATTC_NOTIFY = const(18)
_IRQ_GATTC_INDICATE = const(19)
_IRQ_GATTS_INDICATE_DONE = const(20)
_IRQ_MTU_EXCHANGED = const(21)
_IRQ_L2CAP_ACCEPT = const(22)
_IRQ_L2CAP_CONNECT = const(23)
_IRQ_L2CAP_DISCONNECT = const(24)
_IRQ_L2CAP_RECV = const(25)
_IRQ_L2CAP_SEND_READY = const(26)
_IRQ_CONNECTION_UPDATE = const(27)
_IRQ_ENCRYPTION_UPDATE = const(28)
_IRQ_GET_SECRET = const(29)
_IRQ_SET_SECRET = const(30)

_ADV_IND = const(0x00)
_ADV_DIRECT_IND = const(0x01)
_ADV_SCAN_IND = const(0x02)
_ADV_NONCONN_IND = const(0x03)

_UART_SERVICE_UUID = ubluetooth.UUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
_UART_RX_CHAR_UUID = ubluetooth.UUID("6E400002-B5A3-F393-E0A9-E50E24DCCA9E")
_UART_TX_CHAR_UUID = ubluetooth.UUID("6E400003-B5A3-F393-E0A9-E50E24DCCA9E")

class BLESimpleCentral:
    def __init__(self, ble):
        self.gamepad_name = None
        self.gamepad_addr_type = None
        self.gamepad_addr = None

        self._ble = ble
        self._ble.active(True)
        self._ble.irq(self.bt_irq)

    def bt_irq(self, event, data):
        if event == _IRQ_SCAN_RESULT:
            # A single scan result.
            addr_type, addr, adv_type, rssi, adv_data = data
            if find_adv_name(adv_data) is None:
                return

            print('Address-Type: ', addr_type)
            print('Address: ', [hex(n) for n in addr])
            print('ADV-Type: ', adv_type)
            print('RSSI in dBm: ', rssi)
            print('ADV-Data: ', [hex(n) for n in adv_data])
            print('ADV-Data (Name): ', find_adv_name(adv_data))  # Xbox Wireless Controller
            print()

            if find_adv_name(adv_data) == "Xbox Wireless Controller":
                self.gamepad_name = find_adv_name(adv_data)
                self.gamepad_addr_type = addr_type
                self.gamepad_addr = addr
                self._ble.gap_scan(None)
                self.connect()
        elif event == _IRQ_SCAN_DONE:
            # Scan duration finished or manually stopped.
            pass
        elif event == _IRQ_PERIPHERAL_CONNECT:
            # A successful gap_connect().
            conn_handle, addr_type, addr = data
            print("connect")
            print(data)
            print('Address-Type: ', addr_type)
            print('Address: ', [hex(n) for n in addr])
            print()
        elif event == _IRQ_PERIPHERAL_DISCONNECT:
            # Connected peripheral has disconnected.
            conn_handle, addr_type, addr = data
            print("disconnect")
            print(data)
            print('Address-Type: ', addr_type)
            print('Address: ', [hex(n) for n in addr])
            print()

    # Find a device advertising the environmental sensor service.
    def scan(self):
        self._ble.gap_scan(5000, 100000, 25000, True)

    def connect(self):
        print(self.gamepad_addr)
        self._ble.gap_connect(self.gamepad_addr_type, self.gamepad_addr)

def find_adv(adv_type, data):
    i = 0
    while i + 1 < len(data):
        ad_structure_len = data[i]
        ad_structure_type = data[i + 1]
        ad_structure_payload = data[i + 2: i + ad_structure_len + 1]
        if ad_structure_type == adv_type:
            return ad_structure_payload
        i += ad_structure_len + 1
    return None

def find_adv_name(data):
    n = find_adv(9, data)
    if n:
        return str(n, 'UTF-8')  # Text
    return None

def demo():
    ble = ubluetooth.BLE()
    central = BLESimpleCentral(ble)

    central.scan()

def demo_connect():
    ble = ubluetooth.BLE()
    central = BLESimpleCentral(ble)

    central.connect()

if __name__ == "__main__":
    demo()

@all @mwinkler @cubelegend ( https://github.com/pybricks/support/issues/140#issuecomment-782938904)

Originally posted by @westurner in https://github.com/pybricks/support/issues/262#issuecomment-1493260385

laurensvalk commented 1 year ago
          @westurner This seems to be especially for XBOX ONE controllers. I'm not 100% sure about the low level stuff, but as far as I know, XBOX controllers use XINPUT while most other controllers use DINPUT (DINPUT beeing HID based?). It won't work with the stadia controller as that one uses DINPUT and it won't work with a PS4 controller because those use BTC and not BLE.

I think that there is a button standard for XINPUT but not really for DINPUT. Of course, one could focus on the most popular gamepads. I don't think that there are many gamepads out there that use BLE.

Originally posted by @Tcm0 in https://github.com/pybricks/support/issues/262#issuecomment-1493262116

laurensvalk commented 1 year ago
          @westurner 

Here is the output of the script (running on stock mindstorm firmware, ble xbox controller): image So connection seems to work

Originally posted by @mwinkler in https://github.com/pybricks/support/issues/262#issuecomment-1493281515

laurensvalk commented 1 year ago
          - [ ] https://github.com/pybricks/support/issues/995

Originally posted by @westurner in https://github.com/pybricks/support/issues/262#issuecomment-1493283886

laurensvalk commented 1 year ago
          Does there need to be a *gamepad* Bluetooth HID in the pybricks firmware?

Originally posted by @westurner in https://github.com/pybricks/support/issues/262#issuecomment-1493287515

gyenesvi commented 1 year ago

Nice to see this issue split off, and some investigation going on, still interested in this one.

@westurner, how far did you get with this? I remember concluding with the PyBricks guys (in a March maybe) that if some basic BT connectivity functionality would be cleaned up in the PyBricks code (along the lines of the code that connects to the lego remote), then we might be able to build on that to experiment with HID gamepad controllers.

In the above example ubluetooth is a micropython module, right? Is that available on PyBricks? Could it be? I remember us discussing with @dlech that the bluetooth module is not available and would be problematic, but maybe ubluetooth could be, so that we can build on it? What's the essential difference?

dlech commented 1 year ago

In MicroPython, ubluetooth is an alias for bluetooth so they are both names for the same module.

gyenesvi commented 1 year ago

Oh, that's a pity, so nothing new there. Has there been any restructuring lately about BLE device connectivity code to make it more generally applicable for other purposes than connecting the lego remote?

dlech commented 1 year ago

No, there haven't been any changes in that regard.

laurensvalk commented 1 year ago

All is not lost though, we're always working on Bluetooth --- @dlech did make significant progress on Bluetooth, just in a different way :wink:

gyenesvi commented 1 year ago

@laurensvalk thanks for the update! It seems to me that the whole ble member is recently added in v3.3, right? Is that something that could gain more functionality in the future, such as some generic scan / connect methods to connect to peripheral devices?

laurensvalk commented 1 year ago

Yes, it's definitely a step in the right direction. Indeed, the scanning procedure has been prepared for more generic use in the future.

mozts2005 commented 1 year ago

If someone can sort this out and support both Xbox series, and PlayStation 5 controllers at minimum. I would make a donation to both pybricks and the individual developer of $500.00 each. Must be completed no later then end of Q1 2024.

gyenesvi commented 1 year ago

@mozts2005 that's a generous offer, thanks! @laurensvalk, any chance of giving this some priority? As I said earlier, if the base BLE connectivity code would be there in some generic reusable form, I'd be happy to help with sorting out higher level HID processing part and @westurner might also be interested in helping in. And I'd not be doing it for the money, would even be okay with donating my part (if any) to PyBricks developers.

I don't know if you guys follow this Eurobricks thread about improving Powered Up stuff, but one of the top things that many people complain about is the lack of proportional physical remote, or any kind of 3rd party alternative that can be used without a smart device, so I'd argue that there is more interest than visible from these discussions here on Github. https://www.eurobricks.com/forum/index.php?/forums/topic/195601-lets-fix-powered-up/

laurensvalk commented 1 year ago

Thanks @mozts2005! Let's see if we can make this happen. Would you mind dropping a note at team@pybricks.com? I'd like to confirm a few details to be sure. Thanks!

Thanks also @gyenesvi for your kind follow-up. We are indeed aware of community comments about this. To drop a bit of a hint, we are actively working on a huge update behind the scenes. It will address another big request from the community (perhaps even more popular than Bluetooth). :smile:

gyenesvi commented 1 year ago

That's promising and interesting to hear @laurensvalk, thanks, and keep us posted about the developments!

gyenesvi commented 7 months ago

What's the resolution of this? I see it marked completed, but with what action? Is the investigation completed, or is the feature actually implemented?

laurensvalk commented 7 months ago

Sorry, that must have happened accidentally as I was moving other linked issues around. Rest assured, this is still on our list :smile:

gyenesvi commented 7 months ago

Okay, thanks!

laurensvalk commented 7 months ago

If someone can sort this out and support both Xbox series, and PlayStation 5 controllers at minimum. I would make a donation to both pybricks and the individual developer of $500.00 each. Must be completed no later then end of Q1 2024.

Originally posted by @mozts2005 in https://github.com/pybricks/support/issues/1024#issuecomment-1712963131

I hope the current version meets your needs partially! No matter what you decide, I hope you enjoy the new update :slightly_smiling_face:

All Xbox series controllers since 2016 are supported. With Technic Hub, Spike Prime Hub, Spike Essential Hub, and Mindstorms Robot Inventor Hub.

Playstation 5 may have to wait until (if) Sony enables BLE in a future update.


Sorry, that must have happened accidentally as I was moving other linked issues around. Rest assured, this is still on our list 😄

So this happened... because the internal preview automatically closed this issue!

:tada: Go check it out! :tada:

preview

westurner commented 7 months ago

FWIU, the 8bitdo USB adapters remap all of the supported controllers to an Xbox controller.

8BitDo controllers and arcade sticks

  • Xbox Series X | S, Xbox One Bluetooth controllers
  • PS5, PS4, PS4 Pro, PS3 controllers
  • Switch Pro, Switch Joy-Con, Wii U Pro, Wiimote

Special Features

  • Vibration support on X-input mode ³
  • 6-axis motion on Switch X-input, D-input, Mac mode, Switch mode Upgradable firmware
  • Virtually lag-free

Steam Input also remaps to what looks like an Xbox controller FWICS.

How much work is there to add additional BLE GATT HIDs so that other controllers work?

On Fri, Feb 16, 2024, 7:25 AM laurensvalk @.***> wrote:

If someone can sort this out and support both Xbox series, and PlayStation 5 controllers at minimum. I would make a donation to both pybricks and the individual developer of $500.00 each. Must be completed no later then end of Q1 2024.

Originally posted by @mozts2005 https://github.com/mozts2005 in #1024 (comment) https://github.com/pybricks/support/issues/1024#issuecomment-1712963131

I hope the current version meets your needs partially! No matter what you decide, I hope you enjoy the new update 🙂

All Xbox series controllers since 2016 are supported. With Technic Hub, Spike Prime Hub, Spike Essential Hub, and Mindstorms Robot Inventor Hub.

Playstation 5 may have to wait until (if) Sony enables BLE in a future update.

Sorry, that must have happened accidentally as I was moving other linked issues around. Rest assured, this is still on our list 😄

So this happened... because the internal preview automatically closed this issue!

🎉 Go check it out! 🎉

[image: preview] https://www.youtube.com/watch?v=fxInp9cutNg

— Reply to this email directly, view it on GitHub https://github.com/pybricks/support/issues/1024#issuecomment-1948294595, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAMNSYPM4SKF7AQFLIP7QTYT5F2LAVCNFSM6AAAAAAWQGT2TKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSNBYGI4TINJZGU . You are receiving this because you were mentioned.Message ID: @.***>

laurensvalk commented 7 months ago

We've generalized the BLE code considerably, so I'm quite open to adding a few more gamepads. My preference would be gamepads that are somewhat mainstream and easy enough to get worldwide.

We had some serious challenges getting the Xbox Controller pairing/bonding process to work with the Technic Hub, and it could still be better. So having HID over BLE is no guarantee, but certainly worth a try.

Tcm0 commented 7 months ago

This is a really nice addition to pybricks. Is rumble supported? I think that it would be funny as a "beeping" for driving backwards or as a distance sensor indication that some object is close to the vehicle. Or maybe for some interactive games that use the hubs.

Regarding future supported controllers: one that's probably widely available is the amazon luna controller. It's often on sale for about half the price. Other ones like the google stadia controller or the steam controller are discontinued. Another BLE controller that's pretty new and will be available for a while is the 8bitdo neogeo wireless controller.

laurensvalk commented 7 months ago

Is rumble supported?

Not yet, but it could be. We have also generalized the internal "bluetooth write" function. We'd just need to figure out the right combination of commands to send. Same thing for the light, which should be full RGB.

Tcm0 commented 7 months ago

Is rumble supported?

Not yet, but it could be. We have also generalized the internal "bluetooth write" function. We'd just need to figure out the right combination of commands to send. Same thing for the light, which should be full RGB.

I think that the light is RGB for the elite 2 controller but not for the other xbox controllers. I'm not sure tho. Some of them also have multiple rumble motors in different positions. This might be a bit too much, but in theory it should be possible to control the motors independently

gyenesvi commented 7 months ago

So this happened... because the internal preview automatically closed this issue!

This is amazing news guys, thanks a lot for implementing this! On top of that the code blocks are also a very nice new feature that makes the use case of the Xbox controller even more easy to start using for many people :)

I have tested it and it works well, even though in the beginning I had some problems with the website; it was loading assets very slowly (images, icons, even text), not showing much (I don't think it was my internet connection, I did a speedtest), and on top of that it occasionally went completely blank, unable to recover, had to reload the page. But it seems it got stabilized over time, probably due to caching..

Anyways, the controller works fine. I'd have a few questions/suggestions though.

laurensvalk commented 7 months ago

It seems currently that it is not supported on the city hub, is that due to HW limitation? Too small memory/processing power?

Its Bluetooth chip firmware (separate from the main firmware) does not appear to have certain security features enabled like bonding, which the Xbox Controller requires. Before I started this endeavor, I did not know that the Technic Hub would, either. I went for it anyway, and I'm glad about it now :)

It took me some time to remember that it disconnects from the Technic Hub when the Xbox controller is connected, and I loose print commands that I wanted to use for debugging

We hope to improve this in the future. Would you mind adding your findings to #1462? It would be great to have more data points.

I saw the motor class has accessible parameters for the pid controller. Can you give some hints what parameters I should tune (in which directions) if I wanted to make the steering more aggressive/responsive?

This would probably be worth asking in a different issue for more detail. You could print the current values and then set double the default kp as a starting point.

As for such calibration, does that need to run every time the hub is turned on?

Technically, no. But the Car class is a new addition intended to make steering cars very easy to do, so we just initialize every time. But motors have an option during init to skip the reset. If your steering motor needs to run less than 360, you could also rely on the absolute encoder value. Feel free to make a dedicated issue about this with suggestions and ideas as well. The main thing would be to add some sort of UI for deciding when to calibrate or not. Only the first time after boot? Only if the button is pressed? And so on.

gyenesvi commented 7 months ago

Would you mind adding your findings to https://github.com/pybricks/support/issues/1462?

Sure, I will do that, good to see that I can experiment with staying connected. Will post the results.

I'll create some issues with proposals for the rest then!

One thing I wonder though is how to use the absolute angle of the motor. There is only an 'angle' query method for the motor class as I see, and that one gets the accumulated position, is that right? Is there no way to directly get the absolute position? The only way I see is resetting the accumulated position to the absolute position, and then querying that..

laurensvalk commented 6 months ago

Since support for other controllers comes up frequently, here is a good overview of BLE models.

The list isn't very long, so we'll probably just keep using Xbox.

image

Tcm0 commented 6 months ago

Since support for other controllers comes up frequently, here is a good overview of BLE models.

The list isn't very long, so we'll probably just keep using Xbox.

It's not complete tho. As far as I know, other BLE controllers are Amazon Luna, 8Bitdo NeoGeo (the other 8Bitdo Controllers are BTC) and multiple mobile ones (Flydigi Apex 2, Steelseries Stratus+, Ipega 9078, Ipega 9083S, Nacon MG-X Pro, Razer Jaiju Mobile, PowerA Moga XP5-A, FlyDigi FeiZhi APEX, Saitake 7007f, asus rog kunai 4, Razer Junglecat, Mad Catz C.T.R.L.R. and more). I think that xbox controllers are by far the most common ones in that list. But there might be cheaper ones.

laurensvalk commented 6 months ago

Good news, we can support the rumble/vibration function too!

The following example roughly covers all the functionality we can get. Suggestions for specific APIs or alternate commands are welcome.

from pybricks.iodevices import XboxController
from pybricks.tools import wait

# Set up all devices.
controller = XboxController()

# All actuators at 100% power for 250ms, delay 150ms, do this 3 times:
controller.rumble(power=100, duration=250, delay=150, count=3)

wait(2000)

# Left handle 10%, right handle 15%, left trigger 100%, right trigger 0%, do it once.
controller.rumble(power=[10, 15, 100, 0], duration=100, delay=0, count=1)

wait(2000)
gyenesvi commented 6 months ago

That's a nice addition! Only wonder if there's a more intuitive/readable way to provide ramble power in an individual way for handles/triggers. Maybe a dictionary to name them explicitly? Also, the parameter 'number' sounds a bit too generic to me, could mean anything. I'd use 'count' or 'times', they sound more intuitive to me, but that may only be my preference.

laurensvalk commented 6 months ago

Only wonder if there's a more intuitive/readable way to provide ramble power in an individual way for handles/triggers. Maybe a dictionary to name them explicitly?

Using a tuple for this sort of thing is common throughout the API, e.g. light brightness for the 4 lights on the ultrasonic sensor, and so on. We usually try to strike a balance between verbosity and ease of use. Tuples have other advantages too, since you can do something like:

# Your own feedback constants
COLLISION = [100, 100, 0, 0]
SPEED_FEEDBACK = [0, 0, 50, 50]

And then use these in different parts of your program with power=SPEED_FEEDBACK instead of repeating left_handle_power=0, right_handle_power=0, left_trigger_power=50, right_trigger_power=50 everywhere.

number is now updated to count.

BertLindeman commented 6 months ago

Tuples have other advantages too, since you can do something like:

Your own feedback constants COLLISION = [100, 100, 0, 0] SPEED_FEEDBACK = [0, 0, 50, 50]

number is now updated to count.

FUN

I tried to get all arguments for the rumble call into one constant. But I did not succeed. An example:

# use a constant tuple plus arguments: WORKS
controller.rumble(COLLISION, 250, 150, 3)
wait(2000)

# This one DOES NOT WORK
COLLISION_PLUS_3 = (COLLISION, 250, 150, 3)
controller.rumble(COLLISION_PLUS_3)

The second one complains:

Traceback (most recent call last):
  File "xbox_rumble_issue_1024.py", line 64, in <module>
TypeError: 'duration' argument required

What obvious notation do I miss? A simple user function could provide defaults like

def do_rumble(power, duration=250, delay=0, count=1):
    controller.rumble(power, duration, delay, count)
laurensvalk commented 6 months ago

I tried toI tried to get all arguments for the rumble call into one constant. get all arguments for the rumble call into one constant.

Why would you like to do that? There is a way to do that in Python by unpacking kwargs with *, but this is unrelated to this rumble API.

A simple user function could provide defaults like

I considered leaving a few values at default, but I'm not sure what those should be in "most" cases.

Any suggestions? duration and delay at 150ms? count at 1 or two?

gyenesvi commented 6 months ago

Using a tuple for this sort of thing is common throughout the API

Sure, if that's the convention then it's fine by me. By the way, that is a list not a tuple, but I guess they are interchangeable here, and in most places in the API?

My other suggestion would have been to add further keyword arguments like left_handle_power as you mention, that would be None by default and would override the power for that one handle if it is provided. This way either or both notations could be used, whichever the user likes. But that might be a bit over-engineered..

number is now updated to count

Great, thanks!

I agree that some defaults could be useful. count should definitely be 1, and in that case delay is irrelevant. Maybe duration and delay both around 250ms, but I have not tested this yet, don't know how it feels.

BTW, where can I access the latest build? I mean not just this one, but in general, how do I find it? I tried yesterday but could not decide which one it is.

What obvious notation do I miss?

You need to write controller.rumble(*COLLISION_PLUS_3), but not sure this approach helps with readability..

BertLindeman commented 6 months ago

Why would you like to do that? There is a way to do that in Python by unpacking kwargs with *, but this is unrelated to this rumble API.

OK, (thanks Victor)

I considered leaving a few values at default, but I'm not sure what those should be in "most" cases.

Any suggestions? duration and delay at 150ms? count at 1 or two?

No suggestions from me, Laurens. Just enjoying the new possibilities.

BertLindeman commented 6 months ago

One more question about the rumble method. Does it wait for all iterations (counts) to be complete? If I do a ruble with count=5 at the end of a program, I feel only two. With a wait(5000) behind it I notice all 5 of them. An example:


ZIG = [100, 100, 0, 0]
controller.rumble(ZIG, 400, 200, 5)
gyenesvi commented 6 months ago

Does it wait for all iterations (counts) to be complete?

That's a good question, and I think it could be useful to have a wait parameter, like other methods. Could it? Otherwise, it may be harder to guess how much waiting is actually required to be inserted afterwards to get just enough to move on with the program after all the rumbles have been done.

laurensvalk commented 6 months ago

that is a list not a tuple

Either will work.

This way either or both notations could be used, whichever the user likes.

As with most things, it's usually better if there is just one way to do one thing. I think you answered it very well :smile: :

But that might be a bit over-engineered..


Does it wait for all iterations (counts) to be complete?

I've been wondering about this, but I haven't decided what it should do.

I'll also have to check what the controller does if you send something before the previous one completes.

BertLindeman commented 6 months ago

I'll also have to check what the controller does if you send something before the previous one completes.

Did a few more after each other:


print("do (ZIG, bla bla)", 1)
controller.rumble(ZIG, 400, 200, 25)
print("do (ZIG, bla bla)", 2)
controller.rumble(ZIG, 400, 200, 25)
print("do (ZIG, bla bla)", 3)
controller.rumble(ZIG, 400, 200, 25)
print("do (ZIG, bla bla)", 4)
controller.rumble(ZIG, 400, 200, 25)
print("do (ZIG, bla bla)", 5)
controller.rumble(ZIG, 400, 200, 25)
print("do (ZIG, bla bla) ready")

Results in one time two rumbles and the prints come out:

do (ZIG, bla bla) 1
do (ZIG, bla bla) 2
do (ZIG, bla bla) 3
do (ZIG, bla bla) 4
do (ZIG, bla bla) 5
do (ZIG, bla bla) ready

Conclude that (all?) rumbles are ignored and the program just runs on. Do you want more testing like this?

laurensvalk commented 6 months ago

I will have a detailed look at it later, but you're probably seeing that the controller disconnects/turns off when the program ends :wink:

You might also want to use the four actuators separately to see if the commands are additive, ignored, or overriding the previous one.

BertLindeman commented 6 months ago

I will have a detailed look at it later, but you're probably seeing that the controller disconnects/turns off when the program ends 😉

That is not the case, I think. The xbox button stays lit until the program ends. Feels like the following rumbles are just skipped. Will test with the separate actuators.

BertLindeman commented 6 months ago

Laurens, make a separate (discuss) item of this rumble testing? This item is already long as it is.

BertLindeman commented 6 months ago

BTW, where can I access the latest build? I mean not just this one, but in general, how do I find it? I tried yesterday but could not decide which one it is.

Victor, does this help you? Go to pybricks-micropython and to the actions tab. There on the left side choose "Build" to see the build tasks on the right side. You can take the build of your choice there. Within the build task, browse down to Artifacts and download the firmware for your hub(s)

Bert

gyenesvi commented 6 months ago

Victor, does this help you?

Thanks, yes, found it. However, I don't see the artifact for the Inventor hub. Is that the same as for the Prime hub?

BertLindeman commented 6 months ago

However, I don't see the artifact for the Inventor hub. Is that the same as for the Prime hub?

They are identical for 99.99999 % Only LEGO apps show the difference.

gyenesvi commented 6 months ago

I have been testing on an Inventor hub, it works okay but here are my findings.

Setting power=100 does not make all actuators rumble (as said above), only the handles, but not the triggers. But that's okay that way I guess for a default, because the trigger rumble feels a bit too much for the base case.

250 ms duration feels like a good default for me.

However, I am getting a lot of connectivity errors:

Often I tried without turning the controller off and on again as it was still blinking. Maybe when I turn it off completely and then on again it becomes more stable, errors happen less often, but still happen.

Don't know if this is because now I am testing with an Inventor hub instead of a technic one (because I wanted to see things printed) and that one stays connected to the computer. Or maybe the same problems would arise on the Technic hub but I would not see the errors being printed.. I could test again when that connectivity issue with the Technic hub is resolved, maybe that solves some of these as well.

BertLindeman commented 6 months ago

Victor, currently I (also) use a spikehub.

Often I tried without turning the controller off and on again as it was still blinking. Maybe when I turn it off completely and then on again it becomes more stable, errors happen less often, but still happen.

In my case the controller always switches off at the end of the program. Maybe you have a long way at the end of the program, or do you stop the program crtl-C in the log window? In the crtl-C case you go into the REPL so the controller knows the program is still running (has a connection) and the controller stays ON.

"RuntimeError: Unknown error": I realized that if I stop the program and restart it and turn on the controller then this can happen

Happens here from time to time.

[EDIT] Once I was wanted at the door and left the test situation unattended. When I came back the hub seemed running, was disconnected from the pc. The controller was OFF. So I needed to stop the hub by long pressing the center button. Resulted if fast blinking of the hub. So needed to remove the battery. Tried to recreate without success.

gyenesvi commented 6 months ago

Maybe you have a long way at the end of the program, or do you stop the program crtl-C in the log window?

Yeah, my test program often contains an infinite loop for the controller, so I have to stop it with the stop button in the browser. I think the controller stays on if the program ends with an error, and then it fails with higher probability the next time when trying to connect then if the program is stopped properly, the controller shuts down and needs to be powered up again.