Feed on

An idea came to me after looking at some Gameboy projects which swapped out the Gameboy PCB for their own custom board – What if we could use any Gameboy (GB, GBC, GBP, GBA, GBA SP) as a game controller, stream that over a wireless connection and use a V-USB to act as a HID keyboard or joystick.

It’s do-able I thought, I’ve already played around with all parts that would make this project, I just have to put it all together (and have some code I can re-use): GBDK to make the rom, either a CPLD or MCU to read the input/data, nRF24L01 or similar for the wireless and V-USB on an Atmel MCU for the joystick input. I was considering using Bluetooth as well but might as well just stick with what I know for the moment.

V-USB Joystick Interface

At first I thought about using V-USB just to output keystrokes as I had done this before with the SATVL however I quickly found that outputting and repeating a keystroke like the right cursor key, doesn’t replicate well into an emulator. Basically it’s just like the key is being pressed and released quickly, so if you wanted to move your player right, it would move them right for a little bit, then stop, move right more, etc. I tried playing around with the code but it was a no go.

I went looking to see how other users implemented their own controllers and came across the USB NES Pad adapter that uses the HID joystick interface.

After looking at the code, the joystick interface was very simple to use and that’s all I really needed, so I grabbed that bit of code along with the Vendor ID & Device ID that they were using and it worked perfect! If you send the right cursor press, it holds it down until you tell it that you have left go of it by sending just the default center pad value. I changed the Vendor/Device ID back to the free V-USB joystick IDs – “10204 (0x27dc) | 5824 (0x16c0) | For USB Joysticks”.

GBDK Program

I was able to re-use most of the Multi-game loader code to make a simple program. All this program needs to do is listen for key inputs and output them to an address with the data we specify on the Gameboy.

Like before, I’m going with 0x7000 as nothing should ever need to write or in this case read from that address as our rom file will only really be under 16KB. We could really use anything from 0x0000 to 0x7FFF because we the Gameboy flash cart I’ll be using won’t have an MBC, this could allow us to use the other addresses for other things.

We might press multiple keys down at once, so each key has it’s own bit that corresponds to it, A is bit 0, B is bit 1, etc, luckily for us there are only 8 keys on the Gameboy which fits perfectly into the usual 8 bit data value that the Gameboy reads/writes on the bus. We also need to send a key (0) even if no key is pressed because otherwise the last key pressed is stored in the V-USB receiver side and it would keep sending that key to the PC.

Having the Gameboy output the key value is easy, just like in the Multi-game loader we output the single data byte at a certain address.

nRF24L01 receiver

I’ve got a fair bit of examples using the nRF24, so it was easy to re-use most of the code to get the wireless link up and running in no time.

As part of the setup, we’ll disable auto acknowledgement and auto re-transmit so that communication will be as quick as possible as it’s only going to be one way and the payload size will just be the 1 byte of data which contains the key presses.

The code on the receiver with the V-USB is pretty simple, after setting up the nRF24, we just check to see if a packet has arrived and read the single byte out of it. We keep polling the USB until it’s ready for us to send data back to the PC, then we just read the byte we received to see what keys are currently being pressed and that’s it!

Gameboy cartridge side

I went with one of my flash cart boards and soldered on the flash chip. I used an ATmega48 which I had in my parts and wired it up to the nRF24 to verify it would send packets when booted up. I changed my ATmega48 so it would remove the divide clock by 8 fuse so that it will run at 8MHz at start up.

Then I wired up the address lines A12-A15 to the ATmega and made it flash an LED when it detected 0x7000. I used PD7 = A15, PD6 = A14, PD5 = A13, PD4 = A12 so we have to AND it with F0 (1111 0000) as we only care about the high 4 bits of that port and then compare that against 0x70. Seemed to work fine so far.

Now it’s time to wire up the 8 data lines, I went with PC0-PC5 (D0-D5), PD0 – D6, PD1 – D7. Like before we just AND the PINC with 0x3F as we only care about the low 6 bits and AND the PIND with 0x03 and shift it left by 6 so we’ll get a complete 8 bit value.

So once 0x7000 is detected, we grab the data that the Gameboy is sending and then transmit that data with the nRF24, the receiver side will receive the data and send the value to the PC.

When testing it out and holding down the A button, it seemed be to hit or miss, sometimes other keys would be pressed as well. I knew it had to be timing related so I analysed the C code into ASM to see how much cycles everything took, I’ve done this a few times before.

As we can see just doing the AND and the compare to PIND and we’ve already lost 2 cycles, then another cycle for the compare.

After a bit of tweaking, we just read the PIND address first, delay 1 cycle so that the data we receive is valid (otherwise it’s sometimes incorrect), read some of the buttons and then the start/select buttons. After everything has been read, we can then do the address comparison and send the keypad data along.

One slightly problem remaining is that the start/select data we read isn’t always correct, I guess by the 4th cycle, it’s already too late.

So what we’ll do is just add in a 16MHz crystal and change the fuse bits to suit. Upon trying it again, all the data being received was glitchy/incorrect. I played around with the nops and 3 nops was enough for the data to be correct. Now the start/select button works perfectly!

Though in thinking about all this after I had the prototype up and working, I should have just made PD0-7 be for the 8-bit data and PC0-3 be for A12-A15, so I’ll make that change to the code and schematic and then we can go back to the 8MHz internal oscillator.

13 Oct 18 Edit: Just built the boards, it looks like there is a little glitch every now and then, if you add the WR pin as PC4 and then check for “if ((readAddr & 0x1F) == 0x07)” that seems to solve it.

Initial testing of the range was about 2-3 metres which wasn’t great at all. Tried switching channels and data rates without success. We do have a ground plane on the bottom of the PCB, so that would likely affect the signal. I lifted the nRF module off the board by 5mm and range is at least 8 metres now (I couldn’t test any further). If range was an issue, I was thinking about switching to the Si4432 which I know gives much more range.

I was concerned about input lag on the V-USB side but upon testing it on Super Mario Land 2, I wasn’t able to notice any lag at all. I’ll have to continue to test other games but it’s looking good so far.

In BGB, for the Joystick to work, you just go to Options > Joypad and configure the game controller.

I’ll might add a small menu to the rom which could allow you to change the receivers address, when the receiver starts up it might listen on a special address for 1 second, maybe also allow for channel changes too.

All in all, this project was a pretty quick turnaround from the idea to the prototype because I had used everything before, took a couple more days to clean up the code, fix some bugs but I will be turning this into a product very soon!


Download Wireless_Gameboy_Controller_v1.0

Leave a Reply