NicoHood / Nintendo

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

[Gameboy Player] Unable to send controller inputs to Gamecube when playing a GBA game. #70

Open andrew171717 opened 1 week ago

andrew171717 commented 1 week ago

Testing with the GamecubeConsole.ino example, inputs are received by games such as Smash Bros Melee perfectly, but when attempting to play the Gameboy Player with a GBA game, the console write functions start randomly failing.

I'll do more research and see if I can figure out what might be happening.

andrew171717 commented 1 week ago

Writing the inputs twice back to back appears to fix this? I'll need to test more to be sure

andrew171717 commented 1 week ago

I'm having trouble making sense of this. I'll be able to send 2-4 inputs and then nothing for a few seconds and then I can send 2-4 inputs again.

Skuzee commented 1 week ago

It's possible you're filling the serial buffer too fast and halting the processor. If the serial buffer is full it will wait until there is room to add it. You could blink a light or something to see if one of the arduinos are stopping.

This might help if you add it work, but it might drop data.

// this example is for the Serial.print
void tryPrint(String dataPackage) { // will only write to the serial buffer if there is room. 
  if ( Serial.availableForWrite() >= dataPackage.length())
    Serial.print(dataPackage); // send data here.
}
andrew171717 commented 1 week ago

I think the Gameboy Player is accessing the controller very differently than almost every other game.

Bare with me, I've been at this for a while today. The serial buffer shouldn't be the problem in this case, I'm only writing controller data to the Arduino upon requesting it (which is only 8 bytes) (I've already successfully played multiple copies of Melee with one controller on two consoles with this code with no issues). And I don't request again until it either times out or gets the data. If it times out, I clear the buffer and throw the led high for a seconds. I have not noticed this happening.

I've tried reworking how I'm getting and sending controller inputs multiple times. I tried sending previous controller reports if I didn't get a new input in a certain amount of microseconds. But it seems like calling GamecubeConsole.write function mid serial message causes some data to be lost.

I was starting to lose hope and randomly tried running the GamecubeConsole.write function more than 2 times (as I read in another discussion some games needed more than one write). I noticed improvments, so I tried more. This makes no sense to me, but maybe it will make sense to someone else. The more times I run GamecubeConsole.write the better my controls got. Running it about 10 times for each controller read allows me to have control over the game for about 3-5 seconds and then I lose control again for about the same amount of time. I kept trying higher and higher write amounts. Running GamecubeConsole.write 80 times seems to be close to the magic number for the Gameboy Player, at least in my scenario. Over 100 causes a little stuttering in control input, only 60 also causes some stuttering.

This monster of a loop seems to solve my problems with the Gameboy Player

for (uint8_t i = 0; i < 80; i++){
      GamecubeConsole.write(report);
}

Here is the full code for my project, I have a host sending the inputs to this Arduino over Serial for anyone who is a bit confused on the random Serial code. This doesn't work in Smash Bros Melee or the Gamecube Dashboard from my testing (those both only need a single write call), but it works perfect for the Gameboy Player. I'm thinking of adding a dial or a switch to my breadboard to allow for adjusting the write amount more easily since it seems to differ between games.

I do think there is more to the Gameboy Player, but I'm not the guy to figure it out. This works for my use case, so I'm not going to mess with it any further (hopefully). The Z menu in the Gameboy Player does not work with these write amounts, but the ingame controls work near perfectly and I don't need the Z button menu anyways. So very strange.

#include "Nintendo.h"
#define WAITLIMIT 1000

// Define a Gamecube Console
CGamecubeConsole GamecubeConsole(2);
Gamecube_Report_t report;

//////////////////////////////////////////// Arduino Uno x4 ////////////////////////////////////////////
/////////////////////////////////////////  Serial to Console ///////////////////////////////////////////
void setup(){
  // Set up debug led
  pinMode(LED_BUILTIN, OUTPUT);

  // Set up serial connection to Mega
  Serial.begin(250000);
}

void loop(){
  Serial.write(true); // Request controller data
  unsigned long startTime = millis(); // Get the current time
  while (Serial.available() < sizeof(Gamecube_Report_t)){ // Wait until we get controller data
    if (millis() - startTime > WAITLIMIT) { // Timeout if we do not recieve the data
      clearBuffer(); // Clear out buffer if anything is there
      digitalWrite(LED_BUILTIN, HIGH);
      delay(1000);
      break; // Exit the loop on a failed request
    }
  }; 

  // Read the controller
  if (Serial.available() == sizeof(Gamecube_Report_t)) {
    uint8_t buffer[sizeof(Gamecube_Report_t)];
    Serial.readBytes(buffer, sizeof(buffer));
    memcpy(&report, buffer, sizeof(Gamecube_Report_t));
    for (uint8_t i = 0; i < 80; i++){
      GamecubeConsole.write(report);
    }
    digitalWrite(LED_BUILTIN, LOW);
  }
  else{
    digitalWrite(LED_BUILTIN, HIGH);
    clearBuffer(); // Corrupt data if larger than 1 report. Clear the buffer
    delay(1000);
  }
}

void clearBuffer(){
  while(Serial.available() > 0){ Serial.read(); }
}
andrew171717 commented 1 week ago

Also worth noting, Pokemon Box also requires around 80 writes per loop. It's not as smooth as the 80 writes to the Gameboy Player, but only writing one or two times is not enough. Furthermore, the writes to the game fail every 0.25 seconds or so (both Pokemon Box and the Gameboy Player), I suspect these games are polling the controller at an undocumented rate in regards to this library (this is speculation on my part). The work around I am using will have to do for now.