You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: extras/AddingControllers.md
+40-31Lines changed: 40 additions & 31 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,27 +6,35 @@ The first step in adding support for your controller is building it a class. The
6
6
7
7
Controller classes live inside the library namespace and inherit from the `ExtensionController` class, which contains methods for communicating with the controllers and manipulating the control surface data. This includes combining multi-byte data and extracting bits that correspond to button presses. To use this you'll need to include the "ExtensionController.h" header, which is in the `internal` source directory.
8
8
9
-
The class name for your controller is going to be the "Shared" version of the class, which uses a reference to port and control data that exists elsewhere - thus it has a `_Shared` suffix. Here is what the start of the `ClassicController_Shared` class looks like:
9
+
The class name for your controller is going to be the "Base" version of the class, which uses a reference to port and control data that exists elsewhere - thus it has a `Base` suffix. Here is what the start of the `ClassicControllerBase` class looks like:
10
10
11
11
```C++
12
12
#include"internal/ExtensionController.h"
13
13
14
14
namespaceNintendoExtensionCtrl {
15
-
class ClassicController_Shared : public ExtensionController {
15
+
class ClassicControllerBase : public ExtensionController {
Note the `using` directive. This just means we're reusing the constructor from the base `ExtensionController` class. Most controller variants don't have their own data, and won't need to define their own constructors.
Some controllers require a little extra handholding when they're first initialized. For example, the `DrawsomeTablet` requires two extra register writes before it will return data. The library has a dedicated virtual function for this called `specificInit`:
Note how the constructors use a reference to an `ExtensionData` class *external* to the controller class itself. You can also see that the controller's identity is passed to the `ExtensionController` class to limit what types of controllers can connect (more on that in a bit).
32
+
This function is called at the end of each `connect()` attempt, and returns 'true' if the controlleris successfully initialized, and 'false' if it is not. For most controllers this function is not necessary and can be omitted entirely.
25
33
26
-
## Step #2: Building Your Data Maps
27
-
The next step is to add the data maps for your controller. These define where the data for each control input lies within in the data array.
34
+
This is also where you would increase the data request size if needed. By default all controllers use the Wiimote 0x37 data reporting mode, which returns 6 bytes of control data starting at register 0x00. The library supports a request size of up to 21 bytes, which can be set using the `setRequestSize` function.
28
35
29
-
All controllers use the Wiimote 0x37 data reporting mode by default, returning 6 bytes of control data starting at offset 0x00. If your controller requires more than 6 bytes of data to function, you will have to call `setRequestSize` with the number of bytes to request. This may be done in the constructor.
36
+
## Step #3: Building Your Data Maps
37
+
The next step is to add the data maps for your controller. These define where the data for each control input lies within in the data array.
30
38
31
39
Each map represents the size and position for all of the data of a control surface (button, joystick, etc.). The library has three data types for this, each with a different purpose:
32
40
@@ -55,7 +63,7 @@ From the `size` and `position` values, the constructor will generate a bitmask t
55
63
ByteMap JoyY = ByteMap(1, 5, 3, 2);
56
64
```
57
65
58
-
If the data is spread out over multiple bytes, just use an array of `ByteMap` structs:
66
+
If the data is spread out over multiple bytes, you can use an array of `ByteMap` structs:
With your control maps in place, you'll now need to add your 'get' functions. These functions are public, and will return their respective control data to the user.
100
108
101
109
Since the library is based around "getting" and working with this control data, I elected early-on to ditch the "get" prefix for simplicity. All functions are just the name of the control input itself. Here are the Classic Controller function declarations for the above maps:
@@ -114,73 +122,74 @@ boolean buttonB() const;
114
122
Since you've already spent the time creating the data maps, the function definitions should be straight-forward. Either call `getControlBit()` passing a `BitMap`, or call `getControlData()` passing your `IndexMap` or `ByteMap` value(s). Here are the function definitions for the above controls:
***Note:** I decided to use two different function names for generic control data and bits, just because the bits are automatically inverted. I might decide to change this in the future, but for now it seems to work fine.*
150
+
***Note:** I decided to use two different function names for generic control data and bits because the bits are automatically inverted. I might decide to change this in the future, but for now it seems to work fine.*
143
151
144
-
## Step #4: Add a `printDebug` Function
145
-
This should be a fun step. Create a `printDebug` function that prints out the values for your controller! Since this should only *ever* be called when debugging, I say go crazy with the formatting. The other controllers use `sprintf`/`snprintf` to make things easy, in spite of the extra overhead.
152
+
## Step #5: Add a `printDebug` Function
153
+
This should be a fun step. Create a `printDebug` function that prints out the values for your controller! Since this should only ever be called when debugging, I say go crazy with the formatting. The other controllers use `sprintf`/`snprintf` to make things easy, in spite of the extra overhead.
146
154
147
155
Here's how the Classic Controller debug line looks:
This includes all of the possible control data: the directional pad, +/- and home buttons, ABXY buttons, left and right joysticks, left and right triggers, and ZL/ZR buttons. You can check [the code](../src/controllers/ClassicController.cpp) to see how it was put together.
152
160
153
-
## Step #5: Add Your Controller's Identity
161
+
## Step #6: Add Your Controller's Identity
154
162
Now that your controller definition is nearly done, it's time to add its identity to the list of available controllers!
155
163
156
164
Open up the [`NXC_Identity.h`](../src/internal/NXC_Identity.h) file and add your controller name to the `ExtensionType` enumeration. Then, modify the `decodeIdentity` function so that it will return your controller's ID if the identity bytes match. You can run the [`IdentifyController`](../examples/Any/IdentifyController/IdentifyController.ino) example to fetch the string of ID bytes.
157
165
158
-
Once that's done, head back to your controller's header file and add that identity value to the constructor. This will limit connections to this specific type and report problems if the type doesn't match.
166
+
Once that's done, head back to your controller's header file. You'll need to create a new function, `getExpectedType`, which returns the identity value you just created. This will limit connections to this specific type and report problems if the type doesn't match.
You will also need to edit the switch statement in the `IdentifyControllers` example to add your controller to the 'switch' statement.
166
175
167
-
## Step #6: Create the Combined Class
168
-
The last step to get your controller working is to create a combined class that inherits from your `_Shared` class and bundles it with a set of extension data to use. This creates an easy to use class for most users who are looking to get data from just one controller. Just copy this line, replacing all instances of `YourController` with your controller's name:
176
+
## Step #7: Create the Combined Class
177
+
The last step to get your controller working is to create a combined class that inherits from your `Base` class and bundles it with a set of extension data to use. This creates an easy to use class for most users who are looking to get data from just one controller. Just copy this line, replacing all instances of `YourController` with your controller's name:
169
178
170
179
```C++
171
180
using YourController = NintendoExtensionCtrl::BuildControllerClass
172
-
<NintendoExtensionCtrl::YourController_Shared>;
181
+
<NintendoExtensionCtrl::YourControllerBase>;
173
182
```
174
183
175
184
Be sure to place this *outside* of the namespace, in the header file. See other controller definitions for reference.
176
185
177
-
## Step #7: Add Examples
186
+
## Step #8: Add Examples
178
187
What use is a good controller definition if no one knows how to use it? You should add some examples showing off how the controller works. I usually like to add two:
179
188
180
189
*`DebugPrint`, which is barebones and only includes connection and the `printDebug` function. This is useful for testing that your controller and all of your data maps are functioning properly.
181
190
*`Demo`, which is longer and includes references to all of the different 'get' function types. This should explain to the user how to access your controller's data, including ranges and any quirks.
182
191
183
-
## Step #8: Library Housekeeping
192
+
## Step #9: Library Housekeeping
184
193
At this point your controller is up and running, and everyone should know how to use it! There's just a little bit of housekeeping left to do to make the controller fit in nicely with the others in the library.
0 commit comments