Hacking libwupc - A WiiU Pro Controller Library for Wii Homebrew Applications

FIX94

Former Staff
OP
Former Staff
Joined
Dec 3, 2009
Messages
7,284
Trophies
0
Age
30
Location
???
XP
11,248
Country
Germany
Hey!
So I finally made a small library which you can implement into your wii homebrew application in order to use the WiiU Pro Controller, I've added a usage.txt file to it.
https://github.com/FIX94/libwupc
Just check out the source to get all the data you need.
Let me know if you find any issues with it, so far I only tested it with one controller so I have no idea if it can even handle multiple controllers properly.
This library is implemented for example into wiiflow, nintendont, postloader, fceugx, snes9xgx and vbagx (get those 3 from my signature).
Have Fun!
 

ShadowOne333

QVID PRO QVO
Editorial Team
Joined
Jan 17, 2013
Messages
12,212
Trophies
2
XP
34,071
Country
Mexico
Hey!
So I finally made a small library which you can implement into your wii homebrew application in order to use the WiiU Pro Controller, I've added a usage.txt file to it.
http://code.google.com/p/libwupc
Just check out the source to get all the data you need.
Let me know if you find any issues with it, so far I only tested it with one controller so I have no idea if it can even handle multiple controllers properly.
Have Fun!

Wow awesome!
Now a lot of homebrew emus and apps will be able to handle the Wii U Pro controller.
THANK YOU FIX! :D
 
  • Like
Reactions: Margen67

daxtsu

Well-Known Member
Member
Joined
Jun 9, 2007
Messages
5,627
Trophies
2
XP
5,194
Country
Antarctica
Thank you for the release. Time to make good use of this.

I ran into a few issues:
1. Maybe I'm missing something, but the analog stick "buttons" (e.g. the buttons similar to L3/R3 on PS3 controllers) don't seem to show up in the WUPCData struct's button member when pressed. Every other button does, however.

2. The button values that the Pro Controller reports don't match up with the Classic Controller like usage.txt suggests (for example, proControllerButtons & CLASSIC_CTRL_BUTTON_RIGHT to read the dpad being pressed to the right will not work). Here's an enum I wrote which has correct values:
Code:
enum ProControllerButtons
{
    PRO_CTRL_BUTTON_START = 0x04000000,
    PRO_CTRL_BUTTON_SELECT = 0x10000000,
    PRO_CTRL_BUTTON_HOME = 0x08000000,
    PRO_CTRL_BUTTON_A = 0x00100000,
    PRO_CTRL_BUTTON_B = 0x00400000,
    PRO_CTRL_BUTTON_Y = 0x00200000,
    PRO_CTRL_BUTTON_X = 0x00080000,
    PRO_CTRL_BUTTON_L = 0x20000000,
    PRO_CTRL_BUTTON_R = 0x02000000,
    PRO_CTRL_BUTTON_ZL = 0x00800000,
    PRO_CTRL_BUTTON_ZR = 0x00040000,
    PRO_CTRL_BUTTON_UP = 0x00010000,
    PRO_CTRL_BUTTON_DOWN = 0x40000000,
    PRO_CTRL_BUTTON_LEFT = 0x00020000,
    PRO_CTRL_BUTTON_RIGHT = 0x80000000,
};

3. It doesn't seem to allow regular Wii remotes to connect correctly, even if there is no Pro Controller connected. Once an application begins, the remote acts like it connects, then the LED turns off and no further response is seen. I'm using the gold Wii remote with built-in MotionPlus that came with the Skyward Sword bundle from a few years ago. The odd thing is I just tested with Nintendont 2.160's loader, which also supports this library, and it doesn't seem to mind having both connected at once.

That was caused by a custom libOGC I was using. I forgot, heh.

3. The player LEDs are not changed properly (e.g. if a Wii remote is already connected as player 1, and a Pro Controller is connected, the Pro Controller also gets the player 1 LED).

4. It doesn't seem to respond to WPAD_Disconnect, or other WPAD related commands, so I'm not really sure how to force the controller to disconnect (for example, for an auto-disconnect battery saver feature) or tell if the controller is connected (besides checking if the WUPCData struct returned from WUPC_Data(int chan) isn't a nullptr).

Other than that, it's working swimmingly.

Here's a sample test homebrew I wrote (based on the libOGC default template) to test the functionality of the controller, maybe it'll help someone learn how to use the library. The only thing it lacks is testing the left/right analog push buttons, since the library doesn't seem to support them yet.

Code:
#include <stdio.h>
#include <stdlib.h>
#include <gccore.h>
#include <wiiuse/wpad.h>
#include <wupc/wupc.h>
#include <stdarg.h>
#include <ogc/lwp_watchdog.h>
 
static void *xfb = NULL;
static GXRModeObj *rmode = NULL;
 
static int HWButton = -1;
 
enum HardwareButtons
{
    RESET_BUTTON,
    POWER_BUTTON
};
 
enum ProControllerButtons
{
    PRO_CTRL_BUTTON_START = 0x04000000,
    PRO_CTRL_BUTTON_SELECT = 0x10000000,
    PRO_CTRL_BUTTON_HOME = 0x08000000,
    PRO_CTRL_BUTTON_A = 0x00100000,
    PRO_CTRL_BUTTON_B = 0x00400000,
    PRO_CTRL_BUTTON_Y = 0x00200000,
    PRO_CTRL_BUTTON_X = 0x00080000,
    PRO_CTRL_BUTTON_L = 0x20000000,
    PRO_CTRL_BUTTON_R = 0x02000000,
    PRO_CTRL_BUTTON_ZL = 0x00800000,
    PRO_CTRL_BUTTON_ZR = 0x00040000,
    PRO_CTRL_BUTTON_UP = 0x00010000,
    PRO_CTRL_BUTTON_DOWN = 0x40000000,
    PRO_CTRL_BUTTON_LEFT = 0x00020000,
    PRO_CTRL_BUTTON_RIGHT = 0x80000000,
};
 
void PowerCallback()
{
    HWButton = POWER_BUTTON;
}
 
void ResetCallback()
{
    HWButton = RESET_BUTTON;
}
 
void print_on_row(const int row, const char * const fmt, ...)
{
    printf("\x1b[%d;0H", row);
    va_list args;
    va_start(args, fmt);
    vprintf(fmt, args);
    va_end(args);
}
 
int main(int argc, char **argv)
{
    // Initialise the video system
    VIDEO_Init();
 
    // This function initialises the attached controllers
    WUPC_Init();
    WPAD_Init();
 
    // Obtain the preferred video mode from the system
    // This will correspond to the settings in the Wii menu
    rmode = VIDEO_GetPreferredMode(NULL);
 
    // Allocate memory for the display in the uncached region
    xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
 
    // Initialise the console, required for printf
    console_init(xfb, 20, 20, rmode->fbWidth, rmode->xfbHeight, rmode->fbWidth*VI_DISPLAY_PIX_SZ);
 
    // Set up the video registers with the chosen mode
    VIDEO_Configure(rmode);
 
    // Tell the video hardware where our display memory is
    VIDEO_SetNextFramebuffer(xfb);
 
    // Make the display visible
    VIDEO_SetBlack(FALSE);
 
    // Flush the video register changes to the hardware
    VIDEO_Flush();
 
    // Wait for Video setup to complete
    VIDEO_WaitVSync();
    if (rmode->viTVMode & VI_NON_INTERLACE) VIDEO_WaitVSync();
 
    SYS_SetPowerCallback(PowerCallback);
    SYS_SetResetCallback(ResetCallback);
 
    int row = 0;
    u32 proControllerButtons = 0;
    s16 proControllerLeftStickX = 0;
    s16 proControllerLeftStickY = 0;
    s16 proControllerRightStickX = 0;
    s16 proControllerRightStickY = 0;
 
    bool exitComboPressed = false;
    bool rumbling = false;
    bool rumble = false;
    bool proControllerIsConnected = false;
    u32 timeToRumble = 0;
 
    struct WUPCData *proControllerData = NULL;
 
    while (!exitComboPressed)
    {
        VIDEO_ClearFrameBuffer(rmode, xfb, COLOR_BLACK);
        VIDEO_SetBlack(FALSE);
 
        // Call WPAD_ScanPads each loop, this reads the latest controller states
        WPAD_ScanPads();
 
        proControllerData = WUPC_Data(0);
        if (proControllerData != NULL)
        {
            proControllerButtons = proControllerData->button;
            proControllerLeftStickX = WUPC_lStickX(0);
            proControllerLeftStickY = WUPC_lStickY(0);
            proControllerRightStickX = WUPC_rStickX(0);
            proControllerRightStickY = WUPC_rStickY(0);
            proControllerIsConnected = true;
        }
        else proControllerIsConnected = false;
 
        exitComboPressed = (proControllerButtons & PRO_CTRL_BUTTON_SELECT)
                        && (proControllerButtons & PRO_CTRL_BUTTON_START);
 
        row = 3;
 
        print_on_row(++row, "Pro Controller is %s", proControllerIsConnected ? "connected" : "not connected. Please connect it to test it.\nOr press reset or power on your console to exit.");
 
        if (proControllerIsConnected)
        {
            print_on_row(row, "Press select and start at the same time on your Pro Controller to exit.");
            print_on_row(++row, "Press L and R at the same time to test rumble.");
            print_on_row(++row, "Raw button status: %08x", proControllerButtons);
            print_on_row(++row, "Left Stick X: %d", proControllerLeftStickX);
            print_on_row(++row, "Left Stick Y : %d", proControllerLeftStickY);
            print_on_row(++row, "Right Stick X: %d", proControllerRightStickX);
            print_on_row(++row, "Right Stick Y : %d", proControllerRightStickY);
            print_on_row(++row, "Select is: %s", proControllerButtons & PRO_CTRL_BUTTON_SELECT ? "pressed" : "not pressed");
            print_on_row(++row, "Start is: %s", proControllerButtons & PRO_CTRL_BUTTON_START ? "pressed" : "not pressed");
            print_on_row(++row, "Home is: %s", proControllerButtons & PRO_CTRL_BUTTON_HOME ? "pressed" : "not pressed");
            print_on_row(++row, "A is: %s", proControllerButtons & PRO_CTRL_BUTTON_A ? "pressed" : "not pressed");
            print_on_row(++row, "B is: %s", proControllerButtons & PRO_CTRL_BUTTON_B ? "pressed" : "not pressed");
            print_on_row(++row, "Y is: %s", proControllerButtons & PRO_CTRL_BUTTON_Y ? "pressed" : "not pressed");
            print_on_row(++row, "X is: %s", proControllerButtons & PRO_CTRL_BUTTON_X ? "pressed" : "not pressed");
            print_on_row(++row, "L is: %s", proControllerButtons & PRO_CTRL_BUTTON_L ? "pressed" : "not pressed");
            print_on_row(++row, "R is: %s", proControllerButtons & PRO_CTRL_BUTTON_R ? "pressed" : "not pressed");
            print_on_row(++row, "ZL is: %s", proControllerButtons & PRO_CTRL_BUTTON_ZL ? "pressed" : "not pressed");
            print_on_row(++row, "ZR is: %s", proControllerButtons & PRO_CTRL_BUTTON_ZR ? "pressed" : "not pressed");
            print_on_row(++row, "Up is: %s", proControllerButtons & PRO_CTRL_BUTTON_UP ? "pressed" : "not pressed");
            print_on_row(++row, "Down is: %s", proControllerButtons & PRO_CTRL_BUTTON_DOWN ? "pressed" : "not pressed");
            print_on_row(++row, "Left is: %s", proControllerButtons & PRO_CTRL_BUTTON_LEFT ? "pressed" : "not pressed");
            print_on_row(++row, "Right is: %s", proControllerButtons & PRO_CTRL_BUTTON_RIGHT ? "pressed" : "not pressed");
 
            rumble = (proControllerButtons & PRO_CTRL_BUTTON_L)
                && (proControllerButtons & PRO_CTRL_BUTTON_R);
 
            if (rumble)
            {
                if (!rumbling)
                {
                    rumbling = true;
                    timeToRumble = ticks_to_millisecs(gettime());
                    WUPC_Rumble(0, true);
                }
            }
 
            // Stop rumbling after 250ms.
            if (ticks_to_millisecs(gettime()) > timeToRumble + 250 && rumbling)
            {
                WUPC_Rumble(0, false);
                rumbling = false;
            }
        }
 
        if (HWButton == POWER_BUTTON)
            SYS_ResetSystem(SYS_POWEROFF_STANDBY, 0, 0);
 
        if (HWButton == RESET_BUTTON)
            SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0);
 
        // Wait for the next frame
        VIDEO_WaitVSync();
    }
 
    WUPC_Shutdown();
    WPAD_Shutdown();
    exit(0);
 
    return 0;
}
 

FIX94

Former Staff
OP
Former Staff
Joined
Dec 3, 2009
Messages
7,284
Trophies
0
Age
30
Location
???
XP
11,248
Country
Germany
1. Maybe I'm missing something, but the analog stick "buttons" (e.g. the buttons similar to L3/R3 on PS3 controllers) don't seem to show up in the WUPCData struct's button member when pressed. Every other button does, however.
Yes that is correct, I dont handle those yet so they fall out right now.
2. The button values that the Pro Controller reports don't match up with the Classic Controller like usage.txt suggests (for example, proControllerButtons & CLASSIC_CTRL_BUTTON_RIGHT to read the dpad being pressed to the right will not work)
You might look at the wrong place then, check out wiiuse/wpad.h, the "WPAD_CLASSIC_BUTTON_" definitions are equal to the wiiu pro controller.
3. The player LEDs are not changed properly (e.g. if a Wii remote is already connected as player 1, and a Pro Controller is connected, the Pro Controller also gets the player 1 LED).
The player LEDs are handled by wiiuse internally so libwupc also has a internal player tracker, so they are separate from each other.
4. It doesn't seem to respond to WPAD_Disconnect, or other WPAD related commands, so I'm not really sure how to force the controller to disconnect (for example, for an auto-disconnect battery saver feature) or tell if the controller is connected (besides checking if the WUPCData struct returned from WUPC_Data(int chan) isn't a nullptr).
Yes thats correct, I still need to add a separate disconnect function, so far only WUPC_Shutdown can do that. Also WUPCData being NULL was intended to check if its connected ;)

Thanks for the feedback, I'll think about an update to add a proper disconnect function and see if I cant add the two buttons pressed in.
 
  • Like
Reactions: Margen67

FIX94

Former Staff
OP
Former Staff
Joined
Dec 3, 2009
Messages
7,284
Trophies
0
Age
30
Location
???
XP
11,248
Country
Germany
just made a tiny update, note that its untested:
-added battery status, left and right stick button, charging and usb connected status to the WUPCData struct
-added WUPC_Disconnect function which should disconnect the controller in the specified channel and update all other pro controller channels
 

daxtsu

Well-Known Member
Member
Joined
Jun 9, 2007
Messages
5,627
Trophies
2
XP
5,194
Country
Antarctica
Thanks for pointing me to the right header. I didn't know there were two sets of CC button masks..kinda weird. Anyway, thanks for the update. Hopefully in the future there will be a way to sync the player states. Until then, may I suggest adding a function like so?

Code:
void WUPC_SetLED(u8 chan, u8 led)
{
    if (chan >= CHAN_MAX || __WUPC_Connected[chan] == NULL)
        return;
 
    if (led >= CHAN_MAX)
       led = CHAN_MAX - 1;
 
    u8 buf[2];
    buf[0] = 0x11;
    buf[1] = __WUPC_LEDState[led];
    bte_senddata(__WUPC_Connected[chan]->sock, buf, 2);
}

Then applications can decide which LED a given Pro Controller can have by simply passing 0-3 in the led variable, to turn whichever player's LED on.
 

FIX94

Former Staff
OP
Former Staff
Joined
Dec 3, 2009
Messages
7,284
Trophies
0
Age
30
Location
???
XP
11,248
Country
Germany
Hopefully in the future there will be a way to sync the player states.

Thats highly unlikely, the wiiuse variable is static so we cannot access it from another library, in order to do that we would need to modify wiiuse which I dont want to do to be honest. Also just setting some player led wont mean that it actually is the player so I dont see any reasoning behind it.
 

daxtsu

Well-Known Member
Member
Joined
Jun 9, 2007
Messages
5,627
Trophies
2
XP
5,194
Country
Antarctica
Well, it's pretty confusing to see more than one controller with the same player LED, considering every other Wii game and homebrew shows players what player number they are that way. I guess I'll just add it myself since it's open source.
 

daxtsu

Well-Known Member
Member
Joined
Jun 9, 2007
Messages
5,627
Trophies
2
XP
5,194
Country
Antarctica
Hold on, are you saying we could have up to 4 Wii remotes and 4 Wii U Pro Controllers connected at once, for a total of up to 8 devices? I thought the Wii's Bluetooth could only handle 6 or something (I forget where I read that, though).
 

FIX94

Former Staff
OP
Former Staff
Joined
Dec 3, 2009
Messages
7,284
Trophies
0
Age
30
Location
???
XP
11,248
Country
Germany
Hold on, are you saying we could have up to 4 Wii remotes and 4 Wii U Pro Controllers connected at once, for a total of up to 8 devices? I thought the Wii's Bluetooth could only handle 6 or something (I forget where I read that, though).

I dont see any reason on why you shouldnt be able to connect multiple devices at once...
 
  • Like
Reactions: daxtsu

daxtsu

Well-Known Member
Member
Joined
Jun 9, 2007
Messages
5,627
Trophies
2
XP
5,194
Country
Antarctica
Well, if that's the case, what you're saying makes sense. I guess I was thinking too much inside the box by being limited to only 4 Bluetooth devices in general. I'm sorry for getting a bit snippy earlier.
 

duffmmann

Well-Known Member
Member
Joined
Mar 11, 2009
Messages
3,966
Trophies
2
XP
2,306
Country
United States
Yes! I can't wait to see this get implemented across my favorite emulators (I'm looking at you WiiSX, SNES9XGX, GenPlusGX, VBAGX, and all the others!)
 

daxtsu

Well-Known Member
Member
Joined
Jun 9, 2007
Messages
5,627
Trophies
2
XP
5,194
Country
Antarctica
Learning about what? This library? Just gotta come out of the Nintendont thread sometimes. :P

As we speak, I'm attempting to add support for this into WiiXplorer. It's probably the application I use the second most after postLoader (I spend most of my time developing or changing files on my USB drive). Shouldn't be too hard to do.
 
  • Like
Reactions: AkiraCast

duffmmann

Well-Known Member
Member
Joined
Mar 11, 2009
Messages
3,966
Trophies
2
XP
2,306
Country
United States
Learning about what? This library? Just gotta come out of the Nintendont thread sometimes. :P

As we speak, I'm attempting to add support for this into WiiXplorer. It's probably the application I use the second most after postLoader (I spend most of my time developing or changing files on my USB drive). Shouldn't be too hard to do.


Not to be rude, but why do you use Wiixplorer to change files on your usb drive? Wouldn't it be easier to do so on your computer. Don't get me wrong, I use WiiXplorer from time to time, but it isn't very often.
 

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
    Psionic Roshambo @ Psionic Roshambo: Hmmm it's been a long time since I messed with Retroarch on the PS3 but probably most of the...