Skip to content

Commit 6f35ba6

Browse files
authored
Merge pull request #2 from dmadison/development
Generic Development 1
2 parents 3448fd9 + 3d11b6e commit 6f35ba6

4 files changed

Lines changed: 131 additions & 17 deletions

File tree

examples/WiiClassicController/WiiClassicController.ino

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,20 @@ ClassicController classic;
3636
void setup() {
3737
classic.begin();
3838

39+
XInput.setTriggerRange(2, 28);
40+
41+
XInput.setRange(JOY_LEFT, 8, 56);
42+
XInput.setRange(JOY_RIGHT, 4, 28);
43+
3944
while (!classic.connect()) {
4045
delay(1000); // Controller not connected
4146
}
4247
}
4348

4449
void loop() {
4550
if(classic.update()) { // Get new data!
46-
XInput.setJoystick(JOY_LEFT, map((classic.leftJoyX() << 2), 0, 255, -32768, 32767), map((classic.leftJoyY() << 2), 0, 255, -32768, 32767));
47-
XInput.setJoystick(JOY_RIGHT, map((classic.rightJoyX() << 3), 0, 255, -32768, 32767), map((classic.rightJoyY() << 3), 0, 255, -32768, 32767));
51+
XInput.setJoystick(JOY_LEFT, classic.leftJoyX(), classic.leftJoyY());
52+
XInput.setJoystick(JOY_RIGHT, classic.rightJoyX(), classic.rightJoyY());
4853

4954
XInput.setButton(BUTTON_A, classic.buttonB());
5055
XInput.setButton(BUTTON_B, classic.buttonA());
@@ -57,19 +62,20 @@ void loop() {
5762

5863
XInput.setDpad(classic.dpadUp(), classic.dpadDown(), classic.dpadLeft(), classic.dpadRight());
5964

60-
XInput.setTrigger(TRIGGER_LEFT, classic.triggerL() << 3);
61-
XInput.setTrigger(TRIGGER_RIGHT, classic.triggerR() << 3);
65+
XInput.setTrigger(TRIGGER_LEFT, classic.triggerL());
66+
XInput.setTrigger(TRIGGER_RIGHT, classic.triggerR());
6267

6368
XInput.setButton(BUTTON_LB, classic.buttonZL());
6469
XInput.setButton(BUTTON_RB, classic.buttonZR());
6570

6671
// XInput.setButton(BUTTON_L3, classic.buttonZL()); // The Classic Controller doesn't have L3 and R3
6772
// XInput.setButton(BUTTON_R3, classic.buttonZR()); // but you can uncomment these to check that they work
68-
69-
XInput.send();
70-
XInput.receive();
7173
}
7274
else { // Data is bad :(
75+
XInput.releaseAll();
7376
classic.reconnect();
7477
}
78+
79+
XInput.send();
80+
XInput.receive();
7581
}

keywords.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ setDpad KEYWORD2
2929
setTrigger KEYWORD2
3030
setJoystick KEYWORD2
3131

32+
releaseAll KEYWORD2
33+
3234
# Receive data
3335
getPlayer KEYWORD2
3436
getRumble KEYWORD2
@@ -42,6 +44,14 @@ getLEDPatternID KEYWORD2
4244
send KEYWORD2
4345
receive KEYWORD2
4446

47+
# Input Ranges
48+
setTriggerRange KEYWORD2
49+
setJoystickRange KEYWORD2
50+
setRange KEYWORD2
51+
52+
# Other
53+
reset KEYWORD2
54+
4555
#######################################
4656
# Instances (KEYWORD2)
4757
#######################################

src/XInput.cpp

Lines changed: 88 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
*/
2626

2727
#include "XInput.h"
28+
#include <limits>
2829

2930
// --------------------------------------------------------
3031
// XInput Button Maps |
@@ -72,7 +73,9 @@ constexpr const XInputMap_Button * getButtonFromEnum(XInputControl ctrl) {
7273
case(BUTTON_Y): return &Map_ButtonY;
7374
case(BUTTON_LB): return &Map_ButtonLB;
7475
case(BUTTON_RB): return &Map_ButtonRB;
76+
case(JOY_LEFT):
7577
case(BUTTON_L3): return &Map_ButtonL3;
78+
case(JOY_RIGHT):
7679
case(BUTTON_R3): return &Map_ButtonR3;
7780
case(BUTTON_START): return &Map_ButtonStart;
7881
case(BUTTON_BACK): return &Map_ButtonBack;
@@ -89,9 +92,15 @@ constexpr const XInputMap_Button * getButtonFromEnum(XInputControl ctrl) {
8992
struct XInputMap_Trigger {
9093
constexpr XInputMap_Trigger(uint8_t i)
9194
: index(i) {}
95+
static const XInputGamepad::Range range;
9296
const uint8_t index;
9397
};
9498

99+
const XInputGamepad::Range XInputMap_Trigger::range = {
100+
std::numeric_limits<uint8_t>::min(), // 0
101+
std::numeric_limits<uint8_t>::max() // 255
102+
};
103+
95104
static const XInputMap_Trigger Map_TriggerLeft(4);
96105
static const XInputMap_Trigger Map_TriggerRight(5);
97106

@@ -111,12 +120,18 @@ constexpr const XInputMap_Trigger * getTriggerFromEnum(XInputControl ctrl) {
111120
struct XInputMap_Joystick {
112121
constexpr XInputMap_Joystick(uint8_t xl, uint8_t xh, uint8_t yl, uint8_t yh)
113122
: x_low(xl), x_high(xh), y_low(yl), y_high(yh) {}
123+
static const XInputGamepad::Range range;
114124
const uint8_t x_low;
115125
const uint8_t x_high;
116126
const uint8_t y_low;
117127
const uint8_t y_high;
118128
};
119129

130+
const XInputGamepad::Range XInputMap_Joystick::range = {
131+
std::numeric_limits<int16_t>::min(), // -32768
132+
std::numeric_limits<int16_t>::max() // 32767
133+
};
134+
120135
static const XInputMap_Joystick Map_JoystickLeft(6, 7, 8, 9);
121136
static const XInputMap_Joystick Map_JoystickRight(10, 11, 12, 13);
122137

@@ -151,8 +166,7 @@ static const XInputMap_Rumble RumbleRight(4, 1); // Small motor
151166
XInputGamepad::XInputGamepad() :
152167
tx(), rumble() // Zero initialize arrays
153168
{
154-
tx[0] = 0x00; // Message type
155-
tx[1] = 0x14; // Packet size
169+
reset();
156170
}
157171

158172
void XInputGamepad::press(XInputControl button) {
@@ -170,40 +184,53 @@ void XInputGamepad::setButton(XInputControl button, boolean state) {
170184
else { tx[buttonData->index] &= ~(buttonData->mask); } // Release
171185
}
172186
else {
173-
setTrigger(button, state ? 255 : 0); // Treat trigger like a button
187+
Range * triggerRange = getRangeFromEnum(button);
188+
if (triggerRange == nullptr) return; // Not a trigger (or joystick, but the trigger function will ignore that)
189+
setTrigger(button, state ? triggerRange->max : triggerRange->min); // Treat trigger like a button
174190
}
175191
}
176192

177-
// To-do: add SOCD cleaner
178193
void XInputGamepad::setDpad(XInputControl pad, boolean state) {
179194
setButton(pad, state);
180195
}
181196

182-
183197
void XInputGamepad::setDpad(boolean up, boolean down, boolean left, boolean right) {
198+
// Simultaneous Opposite Cardinal Directions (SOCD) Cleaner
199+
if (up && down) { down = false; } // Up + Down = Up
200+
if (left && right) { left = false; right = false; } // Left + Right = Neutral
201+
184202
setDpad(DPAD_UP, up);
185203
setDpad(DPAD_DOWN, down);
186204
setDpad(DPAD_LEFT, left);
187205
setDpad(DPAD_RIGHT, right);
188206
}
189207

190-
void XInputGamepad::setTrigger(XInputControl trigger, uint8_t val) {
208+
void XInputGamepad::setTrigger(XInputControl trigger, int32_t val) {
191209
const XInputMap_Trigger * triggerData = getTriggerFromEnum(trigger);
192210
if (triggerData == nullptr) return; // Not a trigger
211+
val = rescaleInput(val, *getRangeFromEnum(trigger), triggerData->range);
193212
tx[triggerData->index] = val;
194213
}
195214

196-
void XInputGamepad::setJoystick(XInputControl joy, int16_t x, int16_t y) {
215+
void XInputGamepad::setJoystick(XInputControl joy, int32_t x, int32_t y) {
197216
const XInputMap_Joystick * joyData = getJoyFromEnum(joy);
198217
if (joyData == nullptr) return; // Not a joystick
199218

219+
x = rescaleInput(x, *getRangeFromEnum(joy), joyData->range);
220+
y = rescaleInput(y, *getRangeFromEnum(joy), joyData->range);
221+
200222
tx[joyData->x_low] = lowByte(x);
201223
tx[joyData->x_high] = highByte(x);
202224

203225
tx[joyData->y_low] = lowByte(y);
204226
tx[joyData->y_high] = highByte(y);
205227
}
206228

229+
void XInputGamepad::releaseAll() {
230+
const uint8_t offset = 2; // Skip message type and packet size
231+
memset(tx + offset, 0x00, sizeof(tx) - offset); // Clear TX array
232+
}
233+
207234
uint8_t XInputGamepad::getPlayer() const {
208235
return player;
209236
}
@@ -277,4 +304,58 @@ void XInputGamepad::parseLED(uint8_t leds) {
277304
}
278305
}
279306

307+
XInputGamepad::Range * XInputGamepad::getRangeFromEnum(XInputControl ctrl) {
308+
switch (ctrl) {
309+
case(TRIGGER_LEFT): return &rangeTrigLeft;
310+
case(TRIGGER_RIGHT): return &rangeTrigRight;
311+
case(JOY_LEFT): return &rangeJoyLeft;
312+
case(JOY_RIGHT): return &rangeJoyRight;
313+
default: return nullptr;
314+
}
315+
}
316+
317+
int32_t XInputGamepad::rescaleInput(int32_t val, Range in, Range out) {
318+
if (in.min == out.min && in.max == out.max) return val; // Ranges identical
319+
if (val <= in.min) return out.min; // Out of range -
320+
if (val >= in.max) return out.max; // Out of range +
321+
return map(val, in.min, in.max, out.min, out.max);
322+
}
323+
324+
void XInputGamepad::setTriggerRange(int32_t rangeMin, int32_t rangeMax) {
325+
setRange(TRIGGER_LEFT, rangeMin, rangeMax);
326+
setRange(TRIGGER_RIGHT, rangeMin, rangeMax);
327+
}
328+
329+
void XInputGamepad::setJoystickRange(int32_t rangeMin, int32_t rangeMax) {
330+
setRange(JOY_LEFT, rangeMin, rangeMax);
331+
setRange(JOY_RIGHT, rangeMin, rangeMax);
332+
}
333+
334+
void XInputGamepad::setRange(XInputControl ctrl, int32_t rangeMin, int32_t rangeMax) {
335+
if (rangeMin >= rangeMax) return; // Error: Max < Min
336+
337+
Range * range = getRangeFromEnum(ctrl);
338+
if (range == nullptr) return; // Not an addressable range
339+
340+
range->min = rangeMin;
341+
range->max = rangeMax;
342+
}
343+
344+
// Resets class back to initial values
345+
void XInputGamepad::reset() {
346+
// Reset control data (tx)
347+
releaseAll(); // Clear TX buffer
348+
tx[0] = 0x00; // Set tx message type
349+
tx[1] = 0x14; // Set tx packet size (20)
350+
351+
// Reset received data (rx)
352+
player = 0; // Not connected, no player
353+
memset(rumble, 0x00, sizeof(rumble)); // Clear rumble values
354+
ledPattern = XInputLEDPattern::Off; // No LEDs on
355+
356+
// Reset rescale ranges
357+
setTriggerRange(XInputMap_Trigger::range.min, XInputMap_Trigger::range.max);
358+
setJoystickRange(XInputMap_Joystick::range.min, XInputMap_Joystick::range.max);
359+
}
360+
280361
XInputGamepad XInput;

src/XInput.h

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,11 @@ class XInputGamepad {
8181
void setDpad(XInputControl pad, boolean state);
8282
void setDpad(boolean up, boolean down, boolean left, boolean right);
8383

84-
void setTrigger(XInputControl trigger, uint8_t val);
84+
void setTrigger(XInputControl trigger, int32_t val);
8585

86-
void setJoystick(XInputControl joy, int16_t x, int16_t y);
86+
void setJoystick(XInputControl joy, int32_t x, int32_t y);
87+
88+
void releaseAll();
8789

8890
// Received Data
8991
uint8_t getPlayer() const; // Player # assigned to the controller (0 is unassigned)
@@ -97,7 +99,17 @@ class XInputGamepad {
9799
// USB IO
98100
void send();
99101
void receive();
100-
102+
103+
// Control Input Ranges
104+
struct Range { int32_t min; int32_t max; };
105+
106+
void setTriggerRange(int32_t rangeMin, int32_t rangeMax);
107+
void setJoystickRange(int32_t rangeMin, int32_t rangeMax);
108+
void setRange(XInputControl ctrl, int32_t rangeMin, int32_t rangeMax);
109+
110+
// Setup
111+
void reset();
112+
101113
private:
102114
static const uint32_t USB_Timeout = 12840; // Packet timeout, in milliseconds
103115
uint8_t tx[20]; // USB transmit data
@@ -107,6 +119,11 @@ class XInputGamepad {
107119
XInputLEDPattern ledPattern; // LED pattern data in, buffered
108120

109121
void parseLED(uint8_t leds); // Parse LED data and set pattern/player data
122+
123+
// Control Input Ranges
124+
Range rangeTrigLeft, rangeTrigRight, rangeJoyLeft, rangeJoyRight;
125+
Range * getRangeFromEnum(XInputControl ctrl);
126+
int32_t rescaleInput(int32_t val, Range in, Range out);
110127
};
111128

112129
extern XInputGamepad XInput;

0 commit comments

Comments
 (0)