USB Host Shield 2.0
XBOXONESParser.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2020 Kristian Sloth Lauszus. All rights reserved.
2 
3  This software may be distributed and modified under the terms of the GNU
4  General Public License version 2 (GPL2) as published by the Free Software
5  Foundation and appearing in the file GPL2.TXT included in the packaging of
6  this file. Please note that GPL2 Section 2[b] requires that all works based
7  on this software must also be made publicly available under the terms of
8  the GPL2 ("Copyleft").
9 
10  Contact information
11  -------------------
12 
13  Kristian Sloth Lauszus
14  Web : https://lauszus.com
15  e-mail : lauszus@gmail.com
16  */
17 
18 #include "XBOXONESParser.h"
19 
20 // To enable serial debugging see "settings.h"
21 //#define PRINTREPORT // Uncomment to print the report send by the Xbox One S Controller
22 
24 const uint8_t XBOX_ONE_S_BUTTONS[] PROGMEM = {
25  UP, // UP
26  RIGHT, // RIGHT
27  DOWN, // DOWN
28  LEFT, // LEFT
29 
30  0x0E, // VIEW
31  0x0F, // MENU
32  0x10, // L3
33  0x11, // R3
34 
35  0, 0, // Skip L2 and R2 as these are analog buttons
36  0x0C, // L1
37  0x0D, // R1
38 
39  0x09, // B
40  0x08, // A
41  0x0A, // X
42  0x0B, // Y
43 };
44 
45 enum DPADEnum {
46  DPAD_OFF = 0x0,
47  DPAD_UP = 0x1,
49  DPAD_RIGHT = 0x3,
51  DPAD_DOWN = 0x5,
53  DPAD_LEFT = 0x7,
54  DPAD_LEFT_UP = 0x8,
55 };
56 
57 bool XBOXONESParser::checkDpad(ButtonEnum b) {
58  switch (b) {
59  case UP:
60  return xboxOneSData.btn.dpad == DPAD_LEFT_UP || xboxOneSData.btn.dpad == DPAD_UP || xboxOneSData.btn.dpad == DPAD_UP_RIGHT;
61  case RIGHT:
62  return xboxOneSData.btn.dpad == DPAD_UP_RIGHT || xboxOneSData.btn.dpad == DPAD_RIGHT || xboxOneSData.btn.dpad == DPAD_RIGHT_DOWN;
63  case DOWN:
64  return xboxOneSData.btn.dpad == DPAD_RIGHT_DOWN || xboxOneSData.btn.dpad == DPAD_DOWN || xboxOneSData.btn.dpad == DPAD_DOWN_LEFT;
65  case LEFT:
66  return xboxOneSData.btn.dpad == DPAD_DOWN_LEFT || xboxOneSData.btn.dpad == DPAD_LEFT || xboxOneSData.btn.dpad == DPAD_LEFT_UP;
67  default:
68  return false;
69  }
70 }
71 
73  if (b == L2)
74  return xboxOneSData.trigger[0];
75  else if (b == R2)
76  return xboxOneSData.trigger[1];
77  else if (b <= LEFT) // Dpad
78  return checkDpad(b);
79  else if (b == XBOX)
80  return xboxButtonState;
81  return xboxOneSData.btn.val & (1UL << pgm_read_byte(&XBOX_ONE_S_BUTTONS[(uint8_t)b]));
82 }
83 
85  if(b == L2) {
86  if(L2Clicked) {
87  L2Clicked = false;
88  return true;
89  }
90  return false;
91  } else if(b == R2) {
92  if(R2Clicked) {
93  R2Clicked = false;
94  return true;
95  }
96  return false;
97  } else if (b == XBOX) {
98  bool click = xboxbuttonClickState;
99  xboxbuttonClickState = 0; // Clear "click" event
100  return click;
101  }
102  uint32_t mask = 1UL << pgm_read_byte(&XBOX_ONE_S_BUTTONS[(uint8_t)b]);
103  bool click = buttonClickState.val & mask;
104  buttonClickState.val &= ~mask; // Clear "click" event
105  return click;
106 }
107 
109  return xboxOneSData.hatValue[(uint8_t)a] - 32768; // Convert to signed integer
110 }
111 
112 void XBOXONESParser::Parse(uint8_t len, uint8_t *buf) {
113  if (len > 1 && buf) {
114 #ifdef PRINTREPORT
115  Notify(PSTR("\r\n"), 0x80);
116  for (uint8_t i = 0; i < len; i++) {
117  D_PrintHex<uint8_t > (buf[i], 0x80);
118  Notify(PSTR(" "), 0x80);
119  }
120 #endif
121 
122  if (buf[0] == 0x01) // Check report ID
123  memcpy(&xboxOneSData, buf + 1, min((uint8_t)(len - 1), MFK_CASTUINT8T sizeof(xboxOneSData)));
124  else if (buf[0] == 0x02) { // This report contains the Xbox button
125  xboxButtonState = buf[1];
126  if(xboxButtonState != xboxOldButtonState) {
127  xboxbuttonClickState = xboxButtonState & ~xboxOldButtonState; // Update click state variable
128  xboxOldButtonState = xboxButtonState;
129  }
130  return;
131  } else if (buf[0] == 0x04) // Heartbeat
132  return;
133  else {
134 #ifdef DEBUG_USB_HOST
135  Notify(PSTR("\r\nUnknown report id: "), 0x80);
136  D_PrintHex<uint8_t > (buf[0], 0x80);
137 #endif
138  return;
139  }
140 
141  if (xboxOneSData.btn.val != oldButtonState.val) { // Check if anything has changed
142  buttonClickState.val = xboxOneSData.btn.val & ~oldButtonState.val; // Update click state variable
143  oldButtonState.val = xboxOneSData.btn.val;
144 
145  // The DPAD buttons does not set the different bits, but set a value corresponding to the buttons pressed, we will simply set the bits ourself
146  uint8_t newDpad = 0;
147  if (checkDpad(UP))
148  newDpad |= 1 << UP;
149  if (checkDpad(RIGHT))
150  newDpad |= 1 << RIGHT;
151  if (checkDpad(DOWN))
152  newDpad |= 1 << DOWN;
153  if (checkDpad(LEFT))
154  newDpad |= 1 << LEFT;
155  if (newDpad != oldDpad) {
156  buttonClickState.dpad = newDpad & ~oldDpad; // Override values
157  oldDpad = newDpad;
158  }
159  }
160 
161  // Handle click detection for triggers
162  if(xboxOneSData.trigger[0] != 0 && triggerOld[0] == 0)
163  L2Clicked = true;
164  triggerOld[0] = xboxOneSData.trigger[0];
165  if(xboxOneSData.trigger[1] != 0 && triggerOld[1] == 0)
166  R2Clicked = true;
167  triggerOld[1] = xboxOneSData.trigger[1];
168  }
169 }
170 
172  uint8_t i;
173  for (i = 0; i < sizeof(xboxOneSData.hatValue) / sizeof(xboxOneSData.hatValue[0]); i++)
174  xboxOneSData.hatValue[i] = 32768; // Center value
175  xboxOneSData.btn.val = 0;
176  oldButtonState.val = 0;
177  for (i = 0; i < sizeof(xboxOneSData.trigger) / sizeof(xboxOneSData.trigger[0]); i++)
178  xboxOneSData.trigger[i] = 0;
179 
180  xboxOneSData.btn.dpad = DPAD_OFF;
181  oldButtonState.dpad = DPAD_OFF;
182  buttonClickState.dpad = 0;
183  oldDpad = 0;
184 };
185 
186 #if 0
187 void XBOXONESParser::setRumbleOn(uint8_t leftTrigger, uint8_t rightTrigger, uint8_t leftMotor, uint8_t rightMotor) {
188  // See: https://lore.kernel.org/patchwork/patch/973394/
189  uint8_t buf[8];
190  buf[0] = 0x0F;//1 << 1 | 1 << 0; // Enable weak and strong feedback
191  buf[1] = leftTrigger;
192  buf[2] = rightTrigger;
193  buf[3] = leftMotor;
194  buf[4] = rightMotor;
195  buf[5] = 255; // Duration of effect in 10 ms
196  buf[6] = 0; // Start delay in 10 ms
197  buf[7] = 255; // Loop count
198  sendOutputReport(buf, sizeof(buf));
199 }
200 #endif
bool getButtonClick(ButtonEnum b)
AnalogHatEnum
DPADEnum
Definition: PS4Parser.cpp:20
int16_t getAnalogHat(AnalogHatEnum a)
#define pgm_read_byte(addr)
uint16_t getButtonPress(ButtonEnum b)
XboxOneSButtons btn
#define Notify(...)
Definition: message.h:51
uint16_t trigger[2]
void Parse(uint8_t len, uint8_t *buf)
ButtonEnum
#define PSTR(str)
#define MFK_CASTUINT8T
Definition: settings.h:194
const uint8_t XBOX_ONE_S_BUTTONS[]
uint16_t hatValue[4]