Skip to content

Commit 468f140

Browse files
committed
Initial commit
1 parent 6227127 commit 468f140

4 files changed

Lines changed: 492 additions & 0 deletions

File tree

keywords.txt

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#######################################
2+
# Syntax Coloring Map For Teensy XInput
3+
#######################################
4+
5+
#######################################
6+
# Datatypes (KEYWORD1)
7+
#######################################
8+
9+
# Library
10+
XInput KEYWORD1
11+
12+
# Classes
13+
XInputGamepad KEYWORD1
14+
15+
# Enums
16+
XInputControl KEYWORD1
17+
XInputLEDPattern KEYWORD1
18+
19+
#######################################
20+
# Methods and Functions (KEYWORD2)
21+
#######################################
22+
23+
# Set Control Data
24+
press KEYWORD2
25+
release KEYWORD2
26+
27+
setButton KEYWORD2
28+
setDpad KEYWORD2
29+
setTrigger KEYWORD2
30+
setJoystick KEYWORD2
31+
32+
# Receive data
33+
getPlayer KEYWORD2
34+
getRumble KEYWORD2
35+
getRumbleLeft KEYWORD2
36+
getRumbleRight KEYWORD2
37+
38+
getLEDPatternID KEYWORD2
39+
getLEDPatternID KEYWORD2
40+
41+
# USB I/O
42+
send KEYWORD2
43+
receive KEYWORD2
44+
45+
#######################################
46+
# Instances (KEYWORD2)
47+
#######################################
48+
49+
#######################################
50+
# Constants (LITERAL1)
51+
#######################################
52+
53+
# XInput Controls
54+
BUTTON_A LITERAL1
55+
BUTTON_B LITERAL1
56+
BUTTON_X LITERAL1
57+
BUTTON_Y LITERAL1
58+
BUTTON_LB LITERAL1
59+
BUTTON_RB LITERAL1
60+
BUTTON_L3 LITERAL1
61+
BUTTON_R3 LITERAL1
62+
BUTTON_START LITERAL1
63+
BUTTON_BACK LITERAL1
64+
BUTTON_LOGO LITERAL1
65+
DPAD_UP LITERAL1
66+
DPAD_DOWN LITERAL1
67+
DPAD_LEFT LITERAL1
68+
DPAD_RIGHT LITERAL1
69+
TRIGGER_LEFT LITERAL1
70+
TRIGGER_RIGHT LITERAL1
71+
JOY_LEFT LITERAL1
72+
JOY_RIGHT LITERAL1
73+
74+
# LED Patterns
75+
Off LITERAL1
76+
Blinking LITERAL1
77+
Flash1 LITERAL1
78+
Flash2 LITERAL1
79+
Flash3 LITERAL1
80+
Flash4 LITERAL1
81+
On1 LITERAL1
82+
On2 LITERAL1
83+
On3 LITERAL1
84+
On4 LITERAL1
85+
Rotating LITERAL1
86+
BlinkOnce LITERAL1
87+
BlinkSlow LITERAL1
88+
Alternating LITERAL1

library.properties

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name=Teensy XInput
2+
version=0.0.1
3+
author=David Madison
4+
maintainer=David Madison
5+
sentence=Library for emulating an Xbox 360 controller with Teensyduino.
6+
paragraph=Library for emulating an Xbox 360 controller with Teensyduino.
7+
category=Communication
8+
url=https://github.com/dmadison/TeensyXInput
9+
architectures=*
10+
includes=XInput.h

src/XInput.cpp

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
/*
2+
* Project Teensy XInput Library
3+
* @author David Madison
4+
* @link github.com/dmadison/TeensyXInput
5+
* @license MIT - Copyright (c) 2018 David Madison
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*
25+
*/
26+
27+
#include "XInput.h"
28+
29+
// --------------------------------------------------------
30+
// XInput Button Maps |
31+
// (Matches ID to tx index with bitmask) |
32+
// --------------------------------------------------------
33+
34+
struct XInputMap_Button {
35+
constexpr XInputMap_Button(uint8_t i, uint8_t o)
36+
: index(i), mask(BuildMask(o)) {}
37+
const uint8_t index;
38+
const uint8_t mask;
39+
40+
private:
41+
constexpr static uint8_t BuildMask(uint8_t offset) {
42+
return (1 << offset); // Bitmask of bit to flip
43+
}
44+
};
45+
46+
static const XInputMap_Button Map_DpadUp(2, 0);
47+
static const XInputMap_Button Map_DpadDown(2, 1);
48+
static const XInputMap_Button Map_DpadLeft(2, 2);
49+
static const XInputMap_Button Map_DpadRight(2, 3);
50+
static const XInputMap_Button Map_ButtonStart(2, 4);
51+
static const XInputMap_Button Map_ButtonBack(2, 5);
52+
static const XInputMap_Button Map_ButtonL3(2, 6);
53+
static const XInputMap_Button Map_ButtonR3(2, 7);
54+
55+
static const XInputMap_Button Map_ButtonLB(3, 0);
56+
static const XInputMap_Button Map_ButtonRB(3, 1);
57+
static const XInputMap_Button Map_ButtonLogo(3, 3);
58+
static const XInputMap_Button Map_ButtonA(3, 4);
59+
static const XInputMap_Button Map_ButtonB(3, 5);
60+
static const XInputMap_Button Map_ButtonX(3, 6);
61+
static const XInputMap_Button Map_ButtonY(3, 7);
62+
63+
constexpr const XInputMap_Button * getButtonFromEnum(XInputControl ctrl) {
64+
switch (ctrl) {
65+
case(DPAD_UP): return &Map_DpadUp;
66+
case(DPAD_DOWN): return &Map_DpadDown;
67+
case(DPAD_LEFT): return &Map_DpadLeft;
68+
case(DPAD_RIGHT): return &Map_DpadRight;
69+
case(BUTTON_A): return &Map_ButtonA;
70+
case(BUTTON_B): return &Map_ButtonB;
71+
case(BUTTON_X): return &Map_ButtonX;
72+
case(BUTTON_Y): return &Map_ButtonY;
73+
case(BUTTON_LB): return &Map_ButtonLB;
74+
case(BUTTON_RB): return &Map_ButtonRB;
75+
case(BUTTON_L3): return &Map_ButtonL3;
76+
case(BUTTON_R3): return &Map_ButtonR3;
77+
case(BUTTON_START): return &Map_ButtonStart;
78+
case(BUTTON_BACK): return &Map_ButtonBack;
79+
case(BUTTON_LOGO): return &Map_ButtonLogo;
80+
default: return nullptr;
81+
}
82+
}
83+
84+
// --------------------------------------------------------
85+
// XInput Trigger Maps |
86+
// (Matches ID to tx index) |
87+
// --------------------------------------------------------
88+
89+
struct XInputMap_Trigger {
90+
constexpr XInputMap_Trigger(uint8_t i)
91+
: index(i) {}
92+
const uint8_t index;
93+
};
94+
95+
static const XInputMap_Trigger Map_TriggerLeft(4);
96+
static const XInputMap_Trigger Map_TriggerRight(5);
97+
98+
constexpr const XInputMap_Trigger * getTriggerFromEnum(XInputControl ctrl) {
99+
switch (ctrl) {
100+
case(TRIGGER_LEFT): return &Map_TriggerLeft;
101+
case(TRIGGER_RIGHT): return &Map_TriggerRight;
102+
default: return nullptr;
103+
}
104+
}
105+
106+
// --------------------------------------------------------
107+
// XInput Joystick Maps |
108+
// (Matches ID to tx x/y high/low indices) |
109+
// --------------------------------------------------------
110+
111+
struct XInputMap_Joystick {
112+
constexpr XInputMap_Joystick(uint8_t xl, uint8_t xh, uint8_t yl, uint8_t yh)
113+
: x_low(xl), x_high(xh), y_low(yl), y_high(yh) {}
114+
const uint8_t x_low;
115+
const uint8_t x_high;
116+
const uint8_t y_low;
117+
const uint8_t y_high;
118+
};
119+
120+
static const XInputMap_Joystick Map_JoystickLeft(6, 7, 8, 9);
121+
static const XInputMap_Joystick Map_JoystickRight(10, 11, 12, 13);
122+
123+
constexpr const XInputMap_Joystick * getJoyFromEnum(XInputControl ctrl) {
124+
switch (ctrl) {
125+
case(JOY_LEFT): return &Map_JoystickLeft;
126+
case(JOY_RIGHT): return &Map_JoystickRight;
127+
default: return nullptr;
128+
}
129+
}
130+
131+
// --------------------------------------------------------
132+
// XInput Rumble Maps |
133+
// (Stores rx index and buffer index for each motor) |
134+
// --------------------------------------------------------
135+
136+
struct XInputMap_Rumble {
137+
constexpr XInputMap_Rumble(uint8_t rIndex, uint8_t bIndex)
138+
: rxIndex(rIndex), bufferIndex(bIndex) {}
139+
const uint8_t rxIndex;
140+
const uint8_t bufferIndex;
141+
};
142+
143+
static const XInputMap_Rumble RumbleLeft(3, 0); // Large motor
144+
static const XInputMap_Rumble RumbleRight(4, 1); // Small motor
145+
146+
147+
// --------------------------------------------------------
148+
// XInputGamepad Class (API) |
149+
// --------------------------------------------------------
150+
151+
XInputGamepad::XInputGamepad() :
152+
tx(), rumble() // Zero initialize arrays
153+
{
154+
tx[0] = 0x00; // Message type
155+
tx[1] = 0x14; // Packet size
156+
}
157+
158+
void XInputGamepad::press(XInputControl button) {
159+
setButton(button, true);
160+
}
161+
162+
void XInputGamepad::release(XInputControl button) {
163+
setButton(button, false);
164+
}
165+
166+
void XInputGamepad::setButton(XInputControl button, boolean state) {
167+
const XInputMap_Button * buttonData = getButtonFromEnum(button);
168+
if (buttonData != nullptr) {
169+
if (state) { tx[buttonData->index] |= buttonData->mask; } // Press
170+
else { tx[buttonData->index] &= ~(buttonData->mask); } // Release
171+
}
172+
else {
173+
setTrigger(button, state ? 255 : 0); // Treat trigger like a button
174+
}
175+
}
176+
177+
// To-do: add SOCD cleaner
178+
void XInputGamepad::setDpad(XInputControl pad, boolean state) {
179+
setButton(pad, state);
180+
}
181+
182+
183+
void XInputGamepad::setDpad(boolean up, boolean down, boolean left, boolean right) {
184+
setDpad(DPAD_UP, up);
185+
setDpad(DPAD_DOWN, down);
186+
setDpad(DPAD_LEFT, left);
187+
setDpad(DPAD_RIGHT, right);
188+
}
189+
190+
void XInputGamepad::setTrigger(XInputControl trigger, uint8_t val) {
191+
const XInputMap_Trigger * triggerData = getTriggerFromEnum(trigger);
192+
if (triggerData == nullptr) return; // Not a trigger
193+
tx[triggerData->index] = val;
194+
}
195+
196+
void XInputGamepad::setJoystick(XInputControl joy, int16_t x, int16_t y) {
197+
const XInputMap_Joystick * joyData = getJoyFromEnum(joy);
198+
if (joyData == nullptr) return; // Not a joystick
199+
200+
tx[joyData->x_low] = lowByte(x);
201+
tx[joyData->x_high] = highByte(x);
202+
203+
tx[joyData->y_low] = lowByte(y);
204+
tx[joyData->y_high] = highByte(y);
205+
}
206+
207+
uint8_t XInputGamepad::getPlayer() const {
208+
return player;
209+
}
210+
211+
uint16_t XInputGamepad::getRumble() const {
212+
return rumble[RumbleLeft.bufferIndex] << 8 | rumble[RumbleRight.bufferIndex];
213+
}
214+
215+
uint8_t XInputGamepad::getRumbleLeft() const {
216+
return rumble[RumbleLeft.bufferIndex];
217+
}
218+
219+
uint8_t XInputGamepad::getRumbleRight() const {
220+
return rumble[RumbleRight.bufferIndex];
221+
}
222+
223+
XInputLEDPattern XInputGamepad::getLEDPattern() const {
224+
return ledPattern;
225+
}
226+
227+
uint8_t XInputGamepad::getLEDPatternID() const {
228+
return (uint8_t)ledPattern;
229+
}
230+
231+
//Send an update packet to the PC
232+
void XInputGamepad::send() {
233+
XInputUSB.send(tx, USB_Timeout);
234+
}
235+
236+
void XInputGamepad::receive() {
237+
if (XInputUSB.available() == 0) {
238+
return; // No packet available
239+
}
240+
241+
// Grab packet and store it in rx array
242+
uint8_t rx[8];
243+
XInputUSB.recv(rx, USB_Timeout);
244+
245+
// Rumble Packet
246+
if ((rx[0] == 0x00) & (rx[1] == 0x08)) {
247+
rumble[RumbleLeft.bufferIndex] = rx[RumbleLeft.rxIndex]; // Big weight (Left grip)
248+
rumble[RumbleRight.bufferIndex] = rx[RumbleRight.rxIndex]; // Small weight (Right grip)
249+
}
250+
// LED Packet
251+
else if (rx[0] == 0x01) {
252+
parseLED(rx[2]);
253+
}
254+
}
255+
256+
void XInputGamepad::parseLED(uint8_t leds) {
257+
if (leds > 0x0D) return; // Not a known pattern
258+
259+
ledPattern = (XInputLEDPattern) leds; // Save pattern
260+
if (ledPattern == XInputLEDPattern::Off || ledPattern == XInputLEDPattern::Blinking) {
261+
player = 0; // Not connected
262+
}
263+
else if (ledPattern == XInputLEDPattern::On1 || ledPattern == XInputLEDPattern::Flash1) {
264+
player = 1;
265+
}
266+
else if (ledPattern == XInputLEDPattern::On2 || ledPattern == XInputLEDPattern::Flash2) {
267+
player = 2;
268+
}
269+
else if (ledPattern == XInputLEDPattern::On3 || ledPattern == XInputLEDPattern::Flash3) {
270+
player = 3;
271+
}
272+
else if (ledPattern == XInputLEDPattern::On4 || ledPattern == XInputLEDPattern::Flash4) {
273+
player = 4;
274+
}
275+
else {
276+
return; // Pattern doesn't affect player #
277+
}
278+
}
279+
280+
XInputGamepad XInput;

0 commit comments

Comments
 (0)