Skip to content

Commit 02f87fa

Browse files
authored
Merge pull request #71 from dmadison/linked-list
Linked list for multiple types
2 parents 9b0b087 + aa0d0ed commit 02f87fa

27 files changed

+739
-490
lines changed

examples/Any/IdentifyController/IdentifyController.ino

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ ExtensionPort controller;
3030

3131
void setup() {
3232
Serial.begin(115200);
33+
while (!Serial); // wait for connection
34+
3335
controller.begin();
3436
controller.connect();
3537

examples/Any/MultipleTypes/MultipleTypes.ino

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -28,36 +28,14 @@
2828

2929
ExtensionPort port; // Port for communicating with extension controllers
3030

31-
Nunchuk::Shared nchuk(port); // Read Nunchuk formatted data from the port
32-
ClassicController::Shared classic(port); // Read Classic Controller formatted data from the port
33-
34-
ExtensionController * controllers[] = { // Array of available controllers, for controller-specific init
35-
&nchuk,
36-
&classic,
37-
};
38-
const int NumControllers = sizeof(controllers) / sizeof(ExtensionController*); // # of controllers, auto-generated
39-
40-
41-
boolean connectController() {
42-
boolean connected = port.connect(); // Connect to the controller
43-
44-
if (connected == true) {
45-
for (int i = 0; i < NumControllers; i++) {
46-
if (controllers[i]->controllerTypeMatches()) { // If this controller is connected...
47-
connected = controllers[i]->specificInit(); // ...run the controller-specific initialization
48-
break;
49-
}
50-
}
51-
}
52-
53-
return connected;
54-
}
31+
ClassicController::Shared classic(port);
32+
Nunchuk::Shared nchuk(port);
5533

5634
void setup() {
5735
Serial.begin(115200);
5836
port.begin(); // init I2C
5937

60-
while (!connectController()) {
38+
while (!port.connect()) {
6139
Serial.println("No controller found!");
6240
delay(1000);
6341
}
@@ -67,21 +45,21 @@ void loop() {
6745
boolean success = port.update(); // Get new data from the controller
6846

6947
if (success == true) { // We've got data!
70-
ExtensionType conType = port.getControllerType();
48+
ExtensionType type = port.getControllerType();
7149

72-
switch (conType) {
50+
switch (type) {
7351
case(ExtensionType::Nunchuk):
7452
nchuk.printDebug();
7553
break;
7654
case(ExtensionType::ClassicController):
7755
classic.printDebug();
7856
break;
7957
default:
80-
Serial.println("Other controller connected!");
58+
break;
8159
}
8260
}
8361
else { // Data is bad :(
84-
while (!connectController()) {
62+
while (!port.connect()) {
8563
Serial.println("Controller Disconnected!");
8664
delay(1000);
8765
}

extras/AddingControllers.md

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,35 @@ The first step in adding support for your controller is building it a class. The
66

77
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.
88

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:
1010

1111
```C++
1212
#include "internal/ExtensionController.h"
1313

1414
namespace NintendoExtensionCtrl {
15-
class ClassicController_Shared : public ExtensionController {
15+
class ClassicControllerBase : public ExtensionController {
1616
public:
17-
ClassicController_Shared(ExtensionData &dataRef) :
18-
ExtensionController(dataRef, ExtensionType::ClassicController) {}
17+
using ExtensionController::ExtensionController;
18+
```
19+
20+
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.
21+
22+
## Step #2: Initializing the Controller
1923
20-
ClassicController_Shared(ExtensionPort &controller) :
21-
ClassicController_Shared(controller.getExtensionData()) {}
24+
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`:
25+
26+
```C++
27+
boolean DrawsomeTabletBase::specificInit() {
28+
return writeRegister(0xFB, 0x01) && writeRegister(0xF0, 0x55);
29+
}
2230
```
2331

24-
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 controller is successfully initialized, and 'false' if it is not. For most controllers this function is not necessary and can be omitted entirely.
2533

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.
2835

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.
3038

3139
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:
3240

@@ -55,7 +63,7 @@ From the `size` and `position` values, the constructor will generate a bitmask t
5563
ByteMap JoyY = ByteMap(1, 5, 3, 2);
5664
```
5765

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:
5967

6068
```C++
6169
ByteMap TriggerL[2] = { ByteMap(2, 2, 5, 2), ByteMap(3, 3, 5, 5) };
@@ -95,7 +103,7 @@ struct Maps {
95103
};
96104
```
97105
98-
## Step #3: Add Your "Get" Functions
106+
## Step #4: Add Your "Get" Functions
99107
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.
100108
101109
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;
114122
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:
115123

116124
```C++
117-
uint8_t ClassicController_Shared::leftJoyX() const {
125+
uint8_t ClassicControllerBase::leftJoyX() const {
118126
return getControlData(Maps::LeftJoyX);
119127
}
120128

121-
uint8_t ClassicController_Shared::leftJoyY() const {
129+
uint8_t ClassicControllerBase::leftJoyY() const {
122130
return getControlData(Maps::LeftJoyY);
123131
}
124132

125-
uint8_t ClassicController_Shared::rightJoyX() const {
133+
uint8_t ClassicControllerBase::rightJoyX() const {
126134
return getControlData(Maps::RightJoyX);
127135
}
128136

129-
uint8_t ClassicController_Shared::rightJoyY() const {
137+
uint8_t ClassicControllerBase::rightJoyY() const {
130138
return getControlData(Maps::RightJoyY);
131139
}
132140

133-
boolean ClassicController_Shared::buttonA() const {
141+
boolean ClassicControllerBase::buttonA() const {
134142
return getControlBit(Maps::ButtonA);
135143
}
136144

137-
boolean ClassicController_Shared::buttonB() const {
145+
boolean ClassicControllerBase::buttonB() const {
138146
return getControlBit(Maps::ButtonB);
139147
}
140148
```
141149

142-
***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.*
143151

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.
146154

147155
Here's how the Classic Controller debug line looks:
148156
```
149157
<^v> | +H- | ABXY L:(32, 32) R:(16, 16) | LT:31X RT:31X Z:LR
150158
```
151159
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.
152160

153-
## Step #5: Add Your Controller's Identity
161+
## Step #6: Add Your Controller's Identity
154162
Now that your controller definition is nearly done, it's time to add its identity to the list of available controllers!
155163

156164
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.
157165

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.
159167

160-
```
161-
ClassicController_Shared(ExtensionData &dataRef) :
162-
ExtensionController(dataRef, ExtensionType::ClassicController) {}
168+
```C++
169+
ExtensionType ClassicControllerBase::getExpectedType() const {
170+
return ExtensionType::ClassicController;
171+
}
163172
```
164173

165174
You will also need to edit the switch statement in the `IdentifyControllers` example to add your controller to the 'switch' statement.
166175

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:
169178

170179
```C++
171180
using YourController = NintendoExtensionCtrl::BuildControllerClass
172-
<NintendoExtensionCtrl::YourController_Shared>;
181+
<NintendoExtensionCtrl::YourControllerBase>;
173182
```
174183

175184
Be sure to place this *outside* of the namespace, in the header file. See other controller definitions for reference.
176185

177-
## Step #7: Add Examples
186+
## Step #8: Add Examples
178187
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:
179188

180189
* `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.
181190
* `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.
182191

183-
## Step #8: Library Housekeeping
192+
## Step #9: Library Housekeeping
184193
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.
185194

186195
### Update the Keyword Map

keywords.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
# Library
1010
NintendoExtensionCtrl KEYWORD1
1111

12-
# Controller Base Classes
13-
ExtensionController KEYWORD1
12+
# Multiple Controller Classes
13+
ExtensionPort KEYWORD1
1414
Shared KEYWORD1
1515

1616
# Wii Controllers
@@ -19,7 +19,7 @@ ClassicController KEYWORD1
1919
GuitarController KEYWORD1
2020
DrumController KEYWORD1
2121
DJTurntableController KEYWORD1
22-
uDrawTablet KEYWORD1
22+
uDrawTablet KEYWORD1
2323
DrawsomeTablet KEYWORD1
2424

2525
# Mini Controllers
@@ -49,6 +49,7 @@ update KEYWORD2
4949

5050
reset KEYWORD2
5151

52+
getExpectedType KEYWORD2
5253
getControllerType KEYWORD2
5354
controllerTypeMatches KEYWORD2
5455

src/NintendoExtensionCtrl.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
* along with this program. If not, see <http://www.gnu.org/licenses/>.
2121
*/
2222

23-
#ifndef NintendoExtensionCtrl_h
24-
#define NintendoExtensionCtrl_h
23+
#ifndef NINTENDOEXTENSIONCTRL_H
24+
#define NINTENDOEXTENSIONCTRL_H
2525

2626
// Controller Base
2727
#include "internal/ExtensionController.h"

0 commit comments

Comments
 (0)