NicoHood / Nintendo

Use Nintendo Controllers with Arduino
http://www.nicohood.de
MIT License
283 stars 56 forks source link

Read timing slow if no controller plugged in. #51

Open bkacjios opened 2 years ago

bkacjios commented 2 years ago

Hey again. I have a weird issue and I'm curious if you could provide any insight on why this is happening.

I converted an old and broken Mayflash 2 port adapater to use an arduino and this library.

It works great, however, one thing I noticed is the polling rate drops significantly if I only have one controller plugged in.

Is there an explanation on why this happens? It would be nice if I didn't have to plug in two controllers just to have a usable device. I tried looking through the assembly of gc_n64_get and noticed there's a mention of a timeout. Can this be adjusted or lowered?

Skuzee commented 2 years ago

It might be related to this delay in https://github.com/NicoHood/Nintendo/blob/master/src/Gamecube_N64.c

` // Initial loop waits for the line to go low.

    // This is especially required when reading the console commands.  

    // When reading controller commands the normal timeout below is fine.  

    // The gamecube will poll for an init command every 66ms if not connected.  

    // If a controller is connected it will be polled every X ms.  

    // 8ms (Smash Melee), 0.7 - 9ms (GC options), 0.6 - 19 ms (Mario Football)  

    // This function will wait for 71.22ms to pull the line low.  

    // This way we ensure to catch at least a single gamecube command if connected.`  

I'm unfamiliar with how to reduce that timeout, and it might cause issues for controller initializaion because the library waits for the controller to respond to know if it's connected.

My suggestion to fix this: A circuit to detect if a controller is physically plugged in: This assumes that the ground pins on your breakout board are not connected to each other. There are two ground pins on the socket. If you were to connect one of the pins, or the outer metal shield to an arduino input, and used the internal pull-up resistor; when a controller is plugged in the input pin would be pulled low (connected to ground via the controller) and the would signal the arduino to start reading that controller.

Skuzee commented 2 years ago

http://www.int03.co.uk/crema/hardware/gamecube/gc-control.html

Here is a pin out of the plug; pins 3 and 4 and the outer shield are all grounds. if you can isolate one of them so they are not connected to each other you can use that as the 'sense' pin.

bkacjios commented 2 years ago

Sadly these little breakout boards for the ports have pins 3 and 4 connected together.

bkacjios commented 2 years ago

Well, I just ended up making a timer that will only call gc_init every second or so if there's no controller connected. Seems to have mostly fixed the issue.

Skuzee commented 2 years ago

Sadly these little breakout boards for the ports have pins 3 and 4 connected together.

So there is not signal generated when the controller is plugged in/turns on. I just double checked with my scope. If possible you might be able to cut the trace between pin 3 and 4 and solder directly to one of them as a sense pin.

bkacjios commented 2 years ago

Good to know. I'll have to keep that in mind when I design a custom PCB for this.

Skuzee commented 2 years ago

Well, I just ended up making a timer that will only call gc_init every second or so if there's no controller connected. Seems to have mostly fixed the issue.

This may reduce communication issues, but if there are not two controllers plugged in then it will still hiccup your program every second while it times out for 70ms. This might cause issues still. 70ms is several typical read cycles.

Perhaps you put a reset button on the outside of the box and only initialize the number of controllers plugged in at startup. When adding a second controller you can press the reset button. Also, if a controller does not respond for a certain amount of time (a few seconds) it could just stop checking for that controller until a reset.

Skuzee commented 2 years ago

@NicoHood It might be worth noting that in my observation of communication with a data analyzer; a connected gamecube controllers is responding to the 3 byte command within <1us. So 71ms timeout might be unnecessary. I know it's based off of the gamecube polling rate, but perhaps it times out after a shorter period and then waits the remaining 71ms to try again in a non-blocking manor. That way the program can continue un-halted.

Maybe controller.Read() can take an optional timeout argument for use cases like this, so someone can specify how long to wait for a response before returning.

bkacjios commented 2 years ago

@Skuzee

Worth noting but, I changed this line

https://github.com/NicoHood/Nintendo/blob/e4130ff38039f125d94c5aee7469724dd2965a56/src/Gamecube_N64.c#L344

to

"ldi %[initialTimeoutCount],1\n" // (1) set the outer timeout

to disable the outer timeout loop, and it seems to work great. I even disabled my timer surrounding gc_init call and there's no slowdown at all.

NicoHood commented 2 years ago

But wouldn't this break the code for communicating with the console?

Skuzee commented 2 years ago

Interesting question. In regards to a use case where the Arduino is a man-in-the middle between the console and the controller: (The scenario might depend on whether the firmware sends data to the console every loop, or if it only sends data on a successful read of the controller.)

console.write is blocking, so it waits to receive a command from the console to reply. On initialization, if the console thinks there is no controller attached it would poll every 70ms. When the Arduino receives the command it polls the controller to receive the data for the next write cycle. If there were no controller attached, the Arduino would not receive a response and assume controller.read failed. The console would send the next poll and not receive a reply and continue to assume the controller is not connected. In the above scenario I would assume the 70ms library timeout might be redundant because the rate of polling is ultimately controlled by the console.

The library works as is, so it might be a non issue.

Skuzee commented 2 years ago

If the Arduino replies to the console with the last known data, regardless of whether controller.read return true, the console would assume there was a connected controller and poll at the usual connected rate. Each subsequent controller.read would attempt to reconnect to the controller if there was no response. I don't think there's any harm in checking if a controller is connected more than once every 70ms. In a normal use case, a assume a connected controller would respond very quickly. There maybe situations with damaged hardware or a poor connection that might inhibit the response speed?

bkacjios commented 2 years ago

But wouldn't this break the code for communicating with the console?

I'm only using this for turning two controllers into a HID joystick devices, so I'm not doing any communication with a console. This allows me to constantly check for a second controller if there's not one plugged in without any performance loss.

NicoHood commented 2 years ago

I'm only using this for turning two controllers

Sure, but there are other people that do use this feature.

I suggest, that you both create a PR that in you opinion fixes this issue as best as possible and then we need a 3rd opinion/tester that checks if that code also works in controller moder (acting as a controller to the console).