Initial commit
This commit is contained in:
commit
7cf833e59f
|
@ -0,0 +1,182 @@
|
||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
# BasedOnStyle: Google
|
||||||
|
AccessModifierOffset: -1
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveMacros: false
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
AlignConsecutiveBitFields: false
|
||||||
|
AlignConsecutiveDeclarations: false
|
||||||
|
AlignEscapedNewlines: Left
|
||||||
|
AlignOperands: Align
|
||||||
|
AlignTrailingComments: true
|
||||||
|
AllowAllArgumentsOnNextLine: true
|
||||||
|
AllowAllConstructorInitializersOnNextLine: true
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
|
AllowShortEnumsOnASingleLine: false
|
||||||
|
AllowShortBlocksOnASingleLine: Never
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: false
|
||||||
|
AllowShortLambdasOnASingleLine: All
|
||||||
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: true
|
||||||
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
|
BinPackArguments: false
|
||||||
|
BinPackParameters: false
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: false
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: Never
|
||||||
|
AfterEnum: false
|
||||||
|
AfterFunction: false
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterObjCDeclaration: false
|
||||||
|
AfterStruct: false
|
||||||
|
AfterUnion: false
|
||||||
|
AfterExternBlock: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
BeforeLambdaBody: false
|
||||||
|
BeforeWhile: false
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: true
|
||||||
|
SplitEmptyRecord: true
|
||||||
|
SplitEmptyNamespace: true
|
||||||
|
BreakBeforeBinaryOperators: None
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
BreakBeforeInheritanceComma: false
|
||||||
|
BreakInheritanceList: BeforeColon
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializersBeforeComma: false
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
BreakAfterJavaFieldAnnotations: false
|
||||||
|
BreakStringLiterals: true
|
||||||
|
ColumnLimit: 200
|
||||||
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
|
CompactNamespaces: false
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
DeriveLineEnding: true
|
||||||
|
DerivePointerAlignment: true
|
||||||
|
DisableFormat: false
|
||||||
|
ExperimentalAutoDetectBinPacking: false
|
||||||
|
FixNamespaceComments: true
|
||||||
|
ForEachMacros:
|
||||||
|
- foreach
|
||||||
|
- Q_FOREACH
|
||||||
|
- BOOST_FOREACH
|
||||||
|
IncludeBlocks: Regroup
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '^<ext/.*\.h>'
|
||||||
|
Priority: 2
|
||||||
|
SortPriority: 0
|
||||||
|
- Regex: '^<.*\.h>'
|
||||||
|
Priority: 1
|
||||||
|
SortPriority: 0
|
||||||
|
- Regex: '^<.*'
|
||||||
|
Priority: 2
|
||||||
|
SortPriority: 0
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 3
|
||||||
|
SortPriority: 0
|
||||||
|
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
||||||
|
IncludeIsMainSourceRegex: ''
|
||||||
|
IndentCaseLabels: true
|
||||||
|
IndentCaseBlocks: false
|
||||||
|
IndentGotoLabels: true
|
||||||
|
IndentPPDirectives: None
|
||||||
|
IndentExternBlock: AfterExternBlock
|
||||||
|
IndentWidth: 4
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
InsertTrailingCommas: None
|
||||||
|
JavaScriptQuotes: Leave
|
||||||
|
JavaScriptWrapImports: true
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
|
MacroBlockBegin: ''
|
||||||
|
MacroBlockEnd: ''
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: None
|
||||||
|
ObjCBinPackProtocolList: Never
|
||||||
|
ObjCBlockIndentWidth: 2
|
||||||
|
ObjCBreakBeforeNestedBlockParam: true
|
||||||
|
ObjCSpaceAfterProperty: false
|
||||||
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
PenaltyBreakAssignment: 2
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 1
|
||||||
|
PenaltyBreakComment: 300
|
||||||
|
PenaltyBreakFirstLessLess: 120
|
||||||
|
PenaltyBreakString: 1000
|
||||||
|
PenaltyBreakTemplateDeclaration: 10
|
||||||
|
PenaltyExcessCharacter: 1000000
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 200
|
||||||
|
PointerAlignment: Left
|
||||||
|
RawStringFormats:
|
||||||
|
- Language: Cpp
|
||||||
|
Delimiters:
|
||||||
|
- cc
|
||||||
|
- CC
|
||||||
|
- cpp
|
||||||
|
- Cpp
|
||||||
|
- CPP
|
||||||
|
- 'c++'
|
||||||
|
- 'C++'
|
||||||
|
CanonicalDelimiter: ''
|
||||||
|
BasedOnStyle: google
|
||||||
|
- Language: TextProto
|
||||||
|
Delimiters:
|
||||||
|
- pb
|
||||||
|
- PB
|
||||||
|
- proto
|
||||||
|
- PROTO
|
||||||
|
EnclosingFunctions:
|
||||||
|
- EqualsProto
|
||||||
|
- EquivToProto
|
||||||
|
- PARSE_PARTIAL_TEXT_PROTO
|
||||||
|
- PARSE_TEST_PROTO
|
||||||
|
- PARSE_TEXT_PROTO
|
||||||
|
- ParseTextOrDie
|
||||||
|
- ParseTextProtoOrDie
|
||||||
|
- ParseTestProto
|
||||||
|
- ParsePartialTestProto
|
||||||
|
CanonicalDelimiter: ''
|
||||||
|
BasedOnStyle: google
|
||||||
|
ReflowComments: false
|
||||||
|
SortIncludes: false
|
||||||
|
SortUsingDeclarations: false
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceAfterTemplateKeyword: true
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeCpp11BracedList: false
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
SpaceInEmptyBlock: false
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 2
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInConditionalStatement: false
|
||||||
|
SpacesInContainerLiterals: true
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
SpaceBeforeSquareBrackets: false
|
||||||
|
Standard: Auto
|
||||||
|
StatementMacros:
|
||||||
|
- Q_UNUSED
|
||||||
|
- QT_REQUIRE_VERSION
|
||||||
|
TabWidth: 4
|
||||||
|
UseCRLF: false
|
||||||
|
UseTab: Always
|
||||||
|
WhitespaceSensitiveMacros:
|
||||||
|
- STRINGIZE
|
||||||
|
- PP_STRINGIZE
|
||||||
|
- BOOST_PP_STRINGIZE
|
||||||
|
...
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
.pio
|
||||||
|
ignore/
|
||||||
|
*.kate-swp
|
|
@ -0,0 +1,3 @@
|
||||||
|
cmake_minimum_required(VERSION 3.16.0)
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(OpenRetroPad)
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
find src -type f \( -iname '*.h' -o -iname '*.c' -o -iname '*.cpp' \) -print0 | xargs -0 clang-format -style=file -i -fallback-style=none
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the usual convention is to give header files names that end with `.h'.
|
||||||
|
It is most portable to use only letters, digits, dashes, and underscores in
|
||||||
|
header file names, and at most one dot.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
|
@ -0,0 +1,46 @@
|
||||||
|
|
||||||
|
This directory is intended for project specific (private) libraries.
|
||||||
|
PlatformIO will compile them to static libraries and link into executable file.
|
||||||
|
|
||||||
|
The source code of each library should be placed in a an own separate directory
|
||||||
|
("lib/your_library_name/[here are source files]").
|
||||||
|
|
||||||
|
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||||
|
|
||||||
|
|--lib
|
||||||
|
| |
|
||||||
|
| |--Bar
|
||||||
|
| | |--docs
|
||||||
|
| | |--examples
|
||||||
|
| | |--src
|
||||||
|
| | |- Bar.c
|
||||||
|
| | |- Bar.h
|
||||||
|
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||||
|
| |
|
||||||
|
| |--Foo
|
||||||
|
| | |- Foo.c
|
||||||
|
| | |- Foo.h
|
||||||
|
| |
|
||||||
|
| |- README --> THIS FILE
|
||||||
|
|
|
||||||
|
|- platformio.ini
|
||||||
|
|--src
|
||||||
|
|- main.c
|
||||||
|
|
||||||
|
and a contents of `src/main.c`:
|
||||||
|
```
|
||||||
|
#include <Foo.h>
|
||||||
|
#include <Bar.h>
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
PlatformIO Library Dependency Finder will find automatically dependent
|
||||||
|
libraries scanning project source files.
|
||||||
|
|
||||||
|
More information about PlatformIO Library Dependency Finder
|
||||||
|
- https://docs.platformio.org/page/librarymanager/ldf.html
|
|
@ -0,0 +1,151 @@
|
||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
# env's are laid out like:
|
||||||
|
# $board-$input-$output
|
||||||
|
# supported values:
|
||||||
|
# $board: micro, esp32
|
||||||
|
# $input: radio, snes, debug
|
||||||
|
# $output: radio, usb, bt, debug
|
||||||
|
# please note not all boards are compatible with all inputs/outputs
|
||||||
|
# for example esp32 can only do bt
|
||||||
|
# micro can only do radio or usb
|
||||||
|
|
||||||
|
[common]
|
||||||
|
# change GAMEPAD_COUNT if you want
|
||||||
|
build_flags = -DGAMEPAD_COUNT=2
|
||||||
|
|
||||||
|
[env]
|
||||||
|
monitor_speed = 115200
|
||||||
|
framework = arduino
|
||||||
|
|
||||||
|
# boards
|
||||||
|
|
||||||
|
[esp32]
|
||||||
|
platform = espressif32
|
||||||
|
board = esp32dev
|
||||||
|
|
||||||
|
[micro]
|
||||||
|
platform = atmelavr
|
||||||
|
board = micro
|
||||||
|
|
||||||
|
# outputs
|
||||||
|
|
||||||
|
[out-debug]
|
||||||
|
build_flags = -DGAMEPAD_OUTPUT=0
|
||||||
|
src_filter = +<gamepad/Debug-Gamepad>
|
||||||
|
|
||||||
|
[out-radio]
|
||||||
|
build_flags = -DGAMEPAD_OUTPUT=1
|
||||||
|
src_filter = +<gamepad/Radio-Gamepad>
|
||||||
|
lib_deps = tmrh20/RF24@^1.3.9
|
||||||
|
|
||||||
|
[out-usb]
|
||||||
|
build_flags = -DGAMEPAD_OUTPUT=2
|
||||||
|
src_filter = +<gamepad/USB-Gamepad>
|
||||||
|
framework = arduino
|
||||||
|
platform = atmelavr
|
||||||
|
|
||||||
|
[out-bt]
|
||||||
|
build_flags = -DGAMEPAD_OUTPUT=3
|
||||||
|
src_filter = +<gamepad/ESP32-BLE-Gamepad>
|
||||||
|
|
||||||
|
# radio input
|
||||||
|
|
||||||
|
[in-radio]
|
||||||
|
extends = out-radio
|
||||||
|
src_filter = -<*> +<RadioReceiver.cpp>
|
||||||
|
build_flags = ${common.build_flags} -DGAMEPAD_INPUT=1
|
||||||
|
|
||||||
|
# this one is a bit crazy, just use esp32-$input-bt directly
|
||||||
|
[env:esp32-radio-bt]
|
||||||
|
extends = esp32, in-radio, out-bt
|
||||||
|
src_filter = ${in-radio.src_filter} ${out-bt.src_filter}
|
||||||
|
build_flags = ${in-radio.build_flags} ${out-bt.build_flags}
|
||||||
|
|
||||||
|
[env:esp32-radio-debug]
|
||||||
|
extends = esp32, in-radio, out-debug
|
||||||
|
src_filter = ${in-radio.src_filter} ${out-debug.src_filter}
|
||||||
|
build_flags = ${in-radio.build_flags} ${out-debug.build_flags}
|
||||||
|
|
||||||
|
[env:micro-radio-usb]
|
||||||
|
extends = micro, in-radio, out-usb
|
||||||
|
src_filter = ${in-radio.src_filter} ${out-usb.src_filter}
|
||||||
|
build_flags = ${in-radio.build_flags} ${out-usb.build_flags}
|
||||||
|
|
||||||
|
[env:micro-radio-debug]
|
||||||
|
extends = micro, in-radio, out-debug
|
||||||
|
src_filter = ${in-radio.src_filter} ${out-debug.src_filter}
|
||||||
|
build_flags = ${in-radio.build_flags} ${out-debug.build_flags}
|
||||||
|
|
||||||
|
# not enough pins for env:micro-radio-radio , plus it'd be crazy
|
||||||
|
|
||||||
|
# snes input
|
||||||
|
|
||||||
|
[in-snes]
|
||||||
|
src_filter = -<*> +<SnesNes.cpp>
|
||||||
|
build_flags = ${common.build_flags} -DGAMEPAD_INPUT=2
|
||||||
|
|
||||||
|
[env:esp32-snes-bt]
|
||||||
|
extends = esp32, in-snes, out-bt
|
||||||
|
src_filter = ${in-snes.src_filter} ${out-bt.src_filter}
|
||||||
|
build_flags = ${in-snes.build_flags} ${out-bt.build_flags}
|
||||||
|
|
||||||
|
[env:esp32-snes-debug]
|
||||||
|
extends = esp32, in-snes, out-debug
|
||||||
|
src_filter = ${in-snes.src_filter} ${out-debug.src_filter}
|
||||||
|
build_flags = ${in-snes.build_flags} ${out-debug.build_flags}
|
||||||
|
|
||||||
|
[env:micro-snes-usb]
|
||||||
|
extends = micro, in-snes, out-usb
|
||||||
|
src_filter = ${in-snes.src_filter} ${out-usb.src_filter}
|
||||||
|
build_flags = ${in-snes.build_flags} ${out-usb.build_flags}
|
||||||
|
|
||||||
|
[env:micro-snes-debug]
|
||||||
|
extends = micro, in-snes, out-debug
|
||||||
|
src_filter = ${in-snes.src_filter} ${out-debug.src_filter}
|
||||||
|
build_flags = ${in-snes.build_flags} ${out-debug.build_flags}
|
||||||
|
|
||||||
|
[env:micro-snes-radio]
|
||||||
|
extends = micro, in-snes, out-radio
|
||||||
|
src_filter = ${in-snes.src_filter} ${out-radio.src_filter}
|
||||||
|
build_flags = ${in-snes.build_flags} ${out-radio.build_flags}
|
||||||
|
|
||||||
|
# debug input
|
||||||
|
|
||||||
|
[in-debug]
|
||||||
|
src_filter = -<*> +<Debug.cpp>
|
||||||
|
build_flags = ${common.build_flags} -DGAMEPAD_INPUT=2
|
||||||
|
|
||||||
|
[env:esp32-debug-bt]
|
||||||
|
extends = esp32, in-debug, out-bt
|
||||||
|
src_filter = ${in-debug.src_filter} ${out-bt.src_filter}
|
||||||
|
build_flags = ${in-debug.build_flags} ${out-bt.build_flags}
|
||||||
|
|
||||||
|
[env:esp32-debug-debug]
|
||||||
|
extends = esp32, in-debug, out-debug
|
||||||
|
src_filter = ${in-debug.src_filter} ${out-debug.src_filter}
|
||||||
|
build_flags = ${in-debug.build_flags} ${out-debug.build_flags}
|
||||||
|
|
||||||
|
[env:micro-debug-usb]
|
||||||
|
extends = micro, in-debug, out-usb
|
||||||
|
src_filter = ${in-debug.src_filter} ${out-usb.src_filter}
|
||||||
|
build_flags = ${in-debug.build_flags} ${out-usb.build_flags}
|
||||||
|
|
||||||
|
[env:micro-debug-debug]
|
||||||
|
extends = micro, in-debug, out-debug
|
||||||
|
src_filter = ${in-debug.src_filter} ${out-debug.src_filter}
|
||||||
|
build_flags = ${in-debug.build_flags} ${out-debug.build_flags}
|
||||||
|
|
||||||
|
[env:micro-debug-radio]
|
||||||
|
extends = micro, in-debug, out-radio
|
||||||
|
src_filter = ${in-debug.src_filter} ${out-radio.src_filter}
|
||||||
|
build_flags = ${in-debug.build_flags} ${out-radio.build_flags}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Override some defaults so BT stack is enabled
|
||||||
|
# and WiFi disabled by default in this example
|
||||||
|
CONFIG_BT_ENABLED=y
|
||||||
|
CONFIG_ESP32_ENABLE_STACK_BT=y
|
||||||
|
|
||||||
|
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
|
||||||
|
|
||||||
|
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
|
||||||
|
CONFIG_BTDM_CTRL_MODE_BTDM=n
|
||||||
|
|
||||||
|
CONFIG_WIFI_ENABLED=n
|
||||||
|
CONFIG_ESP32_ENABLE_STACK_WIFI=n
|
||||||
|
CONFIG_ESP32_WIFI_SW_COEXIST_ENABLE=n
|
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
#include "gamepad/Gamepad.h"
|
||||||
|
|
||||||
|
Gamepad gamepad;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println("Starting debug work!");
|
||||||
|
gamepad.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if (gamepad.isConnected()) {
|
||||||
|
// test code to automate sending to test RadioReceiver (or any gamepad impl)
|
||||||
|
uint8_t c = 0;
|
||||||
|
Serial.println("Press buttons 1 and 32. Move all axes to center. Set DPAD to down right.");
|
||||||
|
gamepad.press(c, BUTTON_1);
|
||||||
|
gamepad.press(c, BUTTON_32);
|
||||||
|
gamepad.setAxes(c, 0, 0, 0, 0, 0, 0, DPAD_DOWN_RIGHT);
|
||||||
|
delay(500);
|
||||||
|
Serial.println("Release all buttons. Move all axes to center. Set DPAD to center.");
|
||||||
|
gamepad.buttons(c, 0);
|
||||||
|
gamepad.setAxes(c, 0, 0, 0, 0, 0, 0, DPAD_CENTER);
|
||||||
|
delay(500);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
#include <SPI.h>
|
||||||
|
#include <nRF24L01.h>
|
||||||
|
#include <RF24.h>
|
||||||
|
|
||||||
|
#define GAMEPAD_REPORT_ARRAY_ADD 1
|
||||||
|
|
||||||
|
#include "gamepad/Gamepad.h"
|
||||||
|
|
||||||
|
RF24 radio(7, 8); // CE, CSN
|
||||||
|
|
||||||
|
const byte address[13] = "OpenRetroPad";
|
||||||
|
|
||||||
|
Gamepad gamepad;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
gamepad.begin();
|
||||||
|
radio.begin();
|
||||||
|
radio.openReadingPipe(0, address);
|
||||||
|
radio.setPALevel(RF24_PA_MIN);
|
||||||
|
radio.startListening();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if (radio.available() && gamepad.isConnected()) {
|
||||||
|
radio.read(&gamepad.gamepadReport, 16);
|
||||||
|
gamepad.sync(gamepad.gamepadReport[15]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,225 @@
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
#define GAMEPAD_COUNT 2
|
||||||
|
#define BUTTON_COUNT 12 // SNES has 12, NES only has 8
|
||||||
|
|
||||||
|
//individual data pin for each controller
|
||||||
|
static const int DATA_PIN[GAMEPAD_COUNT] = {18, 19}; // grey, 20, 21 are the next logical ones
|
||||||
|
|
||||||
|
//shared pins between all controllers
|
||||||
|
#if defined(CONFIG_BT_ENABLED)
|
||||||
|
// ESP32
|
||||||
|
static const int LATCH_PIN = 16; // brown
|
||||||
|
static const int CLOCK_PIN = 17; // white
|
||||||
|
#else
|
||||||
|
// micro
|
||||||
|
static const int LATCH_PIN = 2; // brown
|
||||||
|
static const int CLOCK_PIN = 3; // white
|
||||||
|
#endif
|
||||||
|
// power red, ground black
|
||||||
|
|
||||||
|
#include "gamepad/Gamepad.h"
|
||||||
|
|
||||||
|
static const int translateToNES[12] = {1, 8, 2, 3, 4, 5, 6, 7, 0, 8, 8, 8};
|
||||||
|
|
||||||
|
class GameControllers {
|
||||||
|
public:
|
||||||
|
enum Type
|
||||||
|
{
|
||||||
|
NES = 0,
|
||||||
|
SNES = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Button
|
||||||
|
{
|
||||||
|
B = 0,
|
||||||
|
Y = 1,
|
||||||
|
SELECT = 2,
|
||||||
|
START = 3,
|
||||||
|
UP = 4,
|
||||||
|
DOWN = 5,
|
||||||
|
LEFT = 6,
|
||||||
|
RIGHT = 7,
|
||||||
|
A = 8,
|
||||||
|
X = 9,
|
||||||
|
L = 10,
|
||||||
|
R = 11,
|
||||||
|
};
|
||||||
|
|
||||||
|
Type types[GAMEPAD_COUNT];
|
||||||
|
int latchPin;
|
||||||
|
int clockPin;
|
||||||
|
int dataPins[GAMEPAD_COUNT];
|
||||||
|
long buttons[GAMEPAD_COUNT][12];
|
||||||
|
|
||||||
|
int changedControllers[GAMEPAD_COUNT];
|
||||||
|
|
||||||
|
///This has to be initialized once for the shared pins latch and clock
|
||||||
|
void init(int latch, int clock) {
|
||||||
|
latchPin = latch;
|
||||||
|
clockPin = clock;
|
||||||
|
pinMode(latchPin, OUTPUT);
|
||||||
|
digitalWrite(latchPin, LOW);
|
||||||
|
pinMode(clockPin, OUTPUT);
|
||||||
|
digitalWrite(clockPin, HIGH);
|
||||||
|
for (int c = 0; c < GAMEPAD_COUNT; c++) {
|
||||||
|
for (int i = 0; i < 12; i++) {
|
||||||
|
buttons[c][i] = -1;
|
||||||
|
}
|
||||||
|
types[c] = NES; // todo: if SNES button is ever pressed, change type to SNES, maybe buttons to manually toggle?
|
||||||
|
dataPins[c] = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///This sets the controller type and initializes its individual data pin
|
||||||
|
void setController(int controller, Type type, int dataPin) {
|
||||||
|
types[controller] = type;
|
||||||
|
dataPins[controller] = dataPin;
|
||||||
|
pinMode(dataPins[controller], INPUT_PULLUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
void poll(void (*controllerChanged)(const int controller)) {
|
||||||
|
memset(changedControllers, 0, GAMEPAD_COUNT);
|
||||||
|
|
||||||
|
digitalWrite(latchPin, HIGH);
|
||||||
|
delayMicroseconds(12);
|
||||||
|
digitalWrite(latchPin, LOW);
|
||||||
|
delayMicroseconds(6);
|
||||||
|
for (int i = 0; i < 12; i++) {
|
||||||
|
for (int c = 0; c < GAMEPAD_COUNT; c++)
|
||||||
|
if (dataPins[c] > -1) {
|
||||||
|
if (digitalRead(dataPins[c])) {
|
||||||
|
if (-1 != buttons[c][i]) {
|
||||||
|
buttons[c][i] = -1;
|
||||||
|
changedControllers[c] = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//++buttons[c][i];
|
||||||
|
//changedControllers[c] = 1;
|
||||||
|
if (0 != buttons[c][i]) {
|
||||||
|
buttons[c][i] = 0;
|
||||||
|
changedControllers[c] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
digitalWrite(clockPin, LOW);
|
||||||
|
delayMicroseconds(6);
|
||||||
|
digitalWrite(clockPin, HIGH);
|
||||||
|
delayMicroseconds(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int c = 0; c < GAMEPAD_COUNT; c++) {
|
||||||
|
// have any buttons changed state?
|
||||||
|
if (1 == changedControllers[c]) {
|
||||||
|
controllerChanged(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int translate(int controller, Button b) const {
|
||||||
|
if (types[controller] == SNES)
|
||||||
|
return b;
|
||||||
|
return translateToNES[b];
|
||||||
|
}
|
||||||
|
|
||||||
|
///returns if button is currently down
|
||||||
|
bool down(int controller, Button b) const {
|
||||||
|
return buttons[controller][translate(controller, b)] >= 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int translateToHid[12] = {
|
||||||
|
BUTTON_B,
|
||||||
|
BUTTON_Y,
|
||||||
|
BUTTON_SELECT,
|
||||||
|
BUTTON_START,
|
||||||
|
DPAD_UP,
|
||||||
|
DPAD_DOWN,
|
||||||
|
DPAD_LEFT,
|
||||||
|
DPAD_RIGHT,
|
||||||
|
BUTTON_A,
|
||||||
|
BUTTON_X,
|
||||||
|
BUTTON_L,
|
||||||
|
BUTTON_R,
|
||||||
|
};
|
||||||
|
|
||||||
|
GameControllers controllers;
|
||||||
|
|
||||||
|
Gamepad gamepad;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
gamepad.begin();
|
||||||
|
|
||||||
|
//initialize shared pins
|
||||||
|
controllers.init(LATCH_PIN, CLOCK_PIN);
|
||||||
|
|
||||||
|
//activate first controller ans set the type to SNES
|
||||||
|
for (int c = 0; c < GAMEPAD_COUNT; c++) {
|
||||||
|
controllers.setController(c, GameControllers::NES, DATA_PIN[c]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
void controllerChangedDebug(const int c) {
|
||||||
|
Serial.print("controllerChanged!!!!: ");
|
||||||
|
Serial.println(c);
|
||||||
|
Serial.print("c: ");
|
||||||
|
Serial.print(c);
|
||||||
|
for (uint8_t btn = 0; btn < 12; btn++) {
|
||||||
|
Serial.print("; ");
|
||||||
|
Serial.print(btn);
|
||||||
|
Serial.print(", ");
|
||||||
|
Serial.print(controllers.buttons[c][controllers.translate(c, static_cast<GameControllers::Button>(btn))]);
|
||||||
|
}
|
||||||
|
Serial.println("");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
void controllerChanged(const int c) {
|
||||||
|
//controllerChangedDebug(c);
|
||||||
|
|
||||||
|
gamepad.buttons(c, 0);
|
||||||
|
// if start and select are held at the same time, send menu and only menu
|
||||||
|
if (controllers.down(c, GameControllers::START) && controllers.down(c, GameControllers::SELECT)) {
|
||||||
|
gamepad.press(c, BUTTON_MENU);
|
||||||
|
} else {
|
||||||
|
// actually send buttons held
|
||||||
|
for (uint8_t btn = 0; btn < 12; btn++) {
|
||||||
|
if (btn > 3 && btn < 8)
|
||||||
|
continue; // skip dpad
|
||||||
|
if (controllers.down(c, static_cast<GameControllers::Button>(btn))) {
|
||||||
|
gamepad.press(c, translateToHid[btn]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (controllers.down(c, GameControllers::DOWN)) {
|
||||||
|
if (controllers.down(c, GameControllers::RIGHT)) {
|
||||||
|
gamepad.setHatSync(c, DPAD_DOWN_RIGHT);
|
||||||
|
} else if (controllers.down(c, GameControllers::LEFT)) {
|
||||||
|
gamepad.setHatSync(c, DPAD_DOWN_LEFT);
|
||||||
|
} else {
|
||||||
|
gamepad.setHatSync(c, DPAD_DOWN);
|
||||||
|
}
|
||||||
|
} else if (controllers.down(c, GameControllers::UP)) {
|
||||||
|
if (controllers.down(c, GameControllers::RIGHT)) {
|
||||||
|
gamepad.setHatSync(c, DPAD_UP_RIGHT);
|
||||||
|
} else if (controllers.down(c, GameControllers::LEFT)) {
|
||||||
|
gamepad.setHatSync(c, DPAD_UP_LEFT);
|
||||||
|
} else {
|
||||||
|
gamepad.setHatSync(c, DPAD_UP);
|
||||||
|
}
|
||||||
|
} else if (controllers.down(c, GameControllers::RIGHT)) {
|
||||||
|
gamepad.setHatSync(c, DPAD_RIGHT);
|
||||||
|
} else if (controllers.down(c, GameControllers::LEFT)) {
|
||||||
|
gamepad.setHatSync(c, DPAD_LEFT);
|
||||||
|
} else {
|
||||||
|
gamepad.setHatSync(c, DPAD_CENTERED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if (gamepad.isConnected()) {
|
||||||
|
controllers.poll(controllerChanged); //read all controllers at once
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef USB_GAMEPAD_H
|
||||||
|
#define USB_GAMEPAD_H
|
||||||
|
|
||||||
|
#include <WString.h>
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
|
class DebugGamepad : public AbstractGamepad {
|
||||||
|
public:
|
||||||
|
DebugGamepad() : AbstractGamepad() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void begin(void) {
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println("DebugGamepad.begin");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAxes(const uint8_t cIdx, int16_t x, int16_t y, int16_t z, int16_t rZ, char rX, char rY, signed char hat) {
|
||||||
|
Serial.println("DebugGamepad.setAxes");
|
||||||
|
AbstractGamepad::setAxes(cIdx, x, y, z, rZ, rX, rY, hat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sync(const uint8_t cIdx) {
|
||||||
|
Serial.print("DebugGamepad.sync: ");
|
||||||
|
Serial.println(cIdx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef DebugGamepad Gamepad;
|
||||||
|
|
||||||
|
#endif // USB_GAMEPAD_H
|
|
@ -0,0 +1,20 @@
|
||||||
|
#include "BleConnectionStatus.h"
|
||||||
|
|
||||||
|
BleConnectionStatus::BleConnectionStatus(void) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void BleConnectionStatus::onConnect(BLEServer *pServer) {
|
||||||
|
this->connected = true;
|
||||||
|
for (int c = 0; c < GAMEPAD_COUNT; c++) {
|
||||||
|
BLE2902 *desc = (BLE2902 *)this->inputGamepad[c]->getDescriptorByUUID(BLEUUID((uint16_t)0x2902));
|
||||||
|
desc->setNotifications(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BleConnectionStatus::onDisconnect(BLEServer *pServer) {
|
||||||
|
this->connected = false;
|
||||||
|
for (int c = 0; c < GAMEPAD_COUNT; c++) {
|
||||||
|
BLE2902 *desc = (BLE2902 *)this->inputGamepad[c]->getDescriptorByUUID(BLEUUID((uint16_t)0x2902));
|
||||||
|
desc->setNotifications(false);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef ESP32_BLE_CONNECTION_STATUS_H
|
||||||
|
#define ESP32_BLE_CONNECTION_STATUS_H
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#if defined(CONFIG_BT_ENABLED)
|
||||||
|
|
||||||
|
#include "BLE2902.h"
|
||||||
|
#include "BLECharacteristic.h"
|
||||||
|
#include <BLEServer.h>
|
||||||
|
|
||||||
|
class BleConnectionStatus : public BLEServerCallbacks {
|
||||||
|
public:
|
||||||
|
BleConnectionStatus(void);
|
||||||
|
bool connected = false;
|
||||||
|
void onConnect(BLEServer *pServer);
|
||||||
|
void onDisconnect(BLEServer *pServer);
|
||||||
|
BLECharacteristic *inputGamepad[GAMEPAD_COUNT];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CONFIG_BT_ENABLED
|
||||||
|
#endif // ESP32_BLE_CONNECTION_STATUS_H
|
|
@ -0,0 +1,115 @@
|
||||||
|
#ifndef ESP32_BLE_GAMEPAD_H
|
||||||
|
#define ESP32_BLE_GAMEPAD_H
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#if defined(CONFIG_BT_ENABLED)
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
|
#include "BLE2902.h"
|
||||||
|
#include "BLEHIDDevice.h"
|
||||||
|
#include "HIDKeyboardTypes.h"
|
||||||
|
#include "HIDTypes.h"
|
||||||
|
#include <BLEDevice.h>
|
||||||
|
#include <BLEServer.h>
|
||||||
|
#include <BLEUtils.h>
|
||||||
|
#include <driver/adc.h>
|
||||||
|
|
||||||
|
#include "BLECharacteristic.h"
|
||||||
|
#include "BLEHIDDevice.h"
|
||||||
|
#include "BleConnectionStatus.h"
|
||||||
|
|
||||||
|
#if defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||||
|
#include "esp32-hal-log.h"
|
||||||
|
#define LOG_TAG ""
|
||||||
|
#else
|
||||||
|
#include "esp_log.h"
|
||||||
|
static const char *LOG_TAG = "BLEDevice";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class BleGamepad : public AbstractGamepad {
|
||||||
|
private:
|
||||||
|
BleConnectionStatus *connectionStatus;
|
||||||
|
BLEHIDDevice *hid;
|
||||||
|
BLECharacteristic *inputGamepad[GAMEPAD_COUNT];
|
||||||
|
void rawAction(uint8_t msg[], char msgSize);
|
||||||
|
static void taskServer(void *pvParameter) {
|
||||||
|
BleGamepad *BleGamepadInstance = (BleGamepad *)pvParameter; // static_cast<BleGamepad *>(pvParameter);
|
||||||
|
BLEDevice::init(BleGamepadInstance->deviceName);
|
||||||
|
BLEServer *pServer = BLEDevice::createServer();
|
||||||
|
pServer->setCallbacks(BleGamepadInstance->connectionStatus);
|
||||||
|
|
||||||
|
BleGamepadInstance->hid = new BLEHIDDevice(pServer);
|
||||||
|
|
||||||
|
for (int c = 0; c < GAMEPAD_COUNT; c++) {
|
||||||
|
BleGamepadInstance->inputGamepad[c] = BleGamepadInstance->hid->inputReport(c); // <-- input REPORTID from report map
|
||||||
|
BleGamepadInstance->connectionStatus->inputGamepad[c] = BleGamepadInstance->inputGamepad[c];
|
||||||
|
}
|
||||||
|
|
||||||
|
BleGamepadInstance->hid->manufacturer()->setValue(BleGamepadInstance->deviceManufacturer);
|
||||||
|
|
||||||
|
// todo: is this change required for 2 inputs???
|
||||||
|
// BleGamepadInstance->hid->pnp(0x01,0x02e5,0xabcc,0x0110);
|
||||||
|
BleGamepadInstance->hid->pnp(0x02, 0x8282, 0x0132, 0x0106);
|
||||||
|
|
||||||
|
BleGamepadInstance->hid->hidInfo(0x00, 0x01);
|
||||||
|
|
||||||
|
BLESecurity *pSecurity = new BLESecurity();
|
||||||
|
|
||||||
|
pSecurity->setAuthenticationMode(ESP_LE_AUTH_BOND);
|
||||||
|
|
||||||
|
BleGamepadInstance->hid->reportMap((uint8_t *)_hidReportDescriptor, sizeof(_hidReportDescriptor));
|
||||||
|
BleGamepadInstance->hid->startServices();
|
||||||
|
|
||||||
|
BleGamepadInstance->onStarted(pServer);
|
||||||
|
|
||||||
|
BLEAdvertising *pAdvertising = pServer->getAdvertising();
|
||||||
|
pAdvertising->setAppearance(HID_GAMEPAD);
|
||||||
|
pAdvertising->addServiceUUID(BleGamepadInstance->hid->hidService()->getUUID());
|
||||||
|
pAdvertising->start();
|
||||||
|
BleGamepadInstance->hid->setBatteryLevel(BleGamepadInstance->batteryLevel);
|
||||||
|
|
||||||
|
ESP_LOGD(LOG_TAG, "Advertising started!");
|
||||||
|
vTaskDelay(portMAX_DELAY); // delay(portMAX_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
uint8_t batteryLevel;
|
||||||
|
std::string deviceManufacturer;
|
||||||
|
std::string deviceName;
|
||||||
|
|
||||||
|
BleGamepad(std::string deviceName = "8BitDo SN30 Pro+", std::string deviceManufacturer = "8Bitdo SF30 Pro", uint8_t batteryLevel = 100) : AbstractGamepad(), hid(0) {
|
||||||
|
this->deviceName = deviceName;
|
||||||
|
this->deviceManufacturer = deviceManufacturer;
|
||||||
|
this->batteryLevel = batteryLevel;
|
||||||
|
this->connectionStatus = new BleConnectionStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void begin(void) {
|
||||||
|
xTaskCreate(this->taskServer, "server", 20000, (void *)this, 5, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sync(const uint8_t cIdx) {
|
||||||
|
if (this->isConnected()) {
|
||||||
|
this->inputGamepad[cIdx]->setValue(gamepadReport, GAMEPAD_REPORT_LEN);
|
||||||
|
this->inputGamepad[cIdx]->notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isConnected(void) {
|
||||||
|
return this->connectionStatus->connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBatteryLevel(uint8_t level) {
|
||||||
|
this->batteryLevel = level;
|
||||||
|
if (hid != 0)
|
||||||
|
this->hid->setBatteryLevel(this->batteryLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void onStarted(BLEServer *pServer){};
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef BleGamepad Gamepad;
|
||||||
|
|
||||||
|
#endif // CONFIG_BT_ENABLED
|
||||||
|
#endif // ESP32_BLE_GAMEPAD_H
|
|
@ -0,0 +1,26 @@
|
||||||
|
|
||||||
|
#if GAMEPAD_OUTPUT == 0
|
||||||
|
|
||||||
|
#include "Debug-Gamepad/DebugGamepad.h"
|
||||||
|
|
||||||
|
#elif GAMEPAD_OUTPUT == 1
|
||||||
|
|
||||||
|
#include "Radio-Gamepad/RadioGamepad.h"
|
||||||
|
|
||||||
|
#elif GAMEPAD_OUTPUT == 2
|
||||||
|
|
||||||
|
#include "USB-Gamepad/UsbGamepad.h"
|
||||||
|
|
||||||
|
#elif GAMEPAD_OUTPUT == 3
|
||||||
|
|
||||||
|
#ifndef CONFIG_BT_ENABLED
|
||||||
|
#error BT output unsupported if CONFIG_BT_ENABLED is not set
|
||||||
|
#endif // not CONFIG_BT_ENABLED
|
||||||
|
|
||||||
|
#include "ESP32-BLE-Gamepad/BleGamepad.h"
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#error Unsupported value for GAMEPAD_OUTPUT, must be 0-3
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,101 @@
|
||||||
|
/* Copyright (c) 2010-2011 mbed.org, MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||||
|
* and associated documentation files (the "Software"), to deal in the Software without
|
||||||
|
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
* substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||||
|
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// adapted from HIDTypes.h from ESP32 lib, search for modified: in this file
|
||||||
|
|
||||||
|
#ifndef USBCLASS_HID_TYPES
|
||||||
|
#define USBCLASS_HID_TYPES
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* */
|
||||||
|
#define HID_VERSION_1_11 (0x0111)
|
||||||
|
|
||||||
|
/* HID Class */
|
||||||
|
#define HID_CLASS (3)
|
||||||
|
|
||||||
|
// modified: redefined from arduino HID.h, about the same, so let's go with their version
|
||||||
|
//#define HID_SUBCLASS_NONE (0)
|
||||||
|
//#define HID_PROTOCOL_NONE (0)
|
||||||
|
#define HID_SUBCLASS_NONE 0
|
||||||
|
#define HID_PROTOCOL_NONE 0
|
||||||
|
|
||||||
|
/* Descriptors */
|
||||||
|
#define HID_DESCRIPTOR (33)
|
||||||
|
#define HID_DESCRIPTOR_LENGTH (0x09)
|
||||||
|
#define REPORT_DESCRIPTOR (34)
|
||||||
|
|
||||||
|
/* Class requests */
|
||||||
|
#define GET_REPORT (0x1)
|
||||||
|
#define GET_IDLE (0x2)
|
||||||
|
#define SET_REPORT (0x9)
|
||||||
|
#define SET_IDLE (0xa)
|
||||||
|
|
||||||
|
/* HID Class Report Descriptor */
|
||||||
|
/* Short items: size is 0, 1, 2 or 3 specifying 0, 1, 2 or 4 (four) bytes */
|
||||||
|
/* of data as per HID Class standard */
|
||||||
|
|
||||||
|
/* Main items */
|
||||||
|
#define HIDINPUT(size) (0x80 | size)
|
||||||
|
#define HIDOUTPUT(size) (0x90 | size)
|
||||||
|
|
||||||
|
// modified: was FEATURE but conflicted with nRF24L01.h FEATURE
|
||||||
|
//#define FEATURE(size) (0xb0 | size)
|
||||||
|
#define HIDFEATURE(size) (0xb0 | size)
|
||||||
|
|
||||||
|
#define COLLECTION(size) (0xa0 | size)
|
||||||
|
#define END_COLLECTION(size) (0xc0 | size)
|
||||||
|
|
||||||
|
/* Global items */
|
||||||
|
#define USAGE_PAGE(size) (0x04 | size)
|
||||||
|
#define LOGICAL_MINIMUM(size) (0x14 | size)
|
||||||
|
#define LOGICAL_MAXIMUM(size) (0x24 | size)
|
||||||
|
#define PHYSICAL_MINIMUM(size) (0x34 | size)
|
||||||
|
#define PHYSICAL_MAXIMUM(size) (0x44 | size)
|
||||||
|
#define UNIT_EXPONENT(size) (0x54 | size)
|
||||||
|
#define UNIT(size) (0x64 | size)
|
||||||
|
#define REPORT_SIZE(size) (0x74 | size) //bits
|
||||||
|
#define REPORT_ID(size) (0x84 | size)
|
||||||
|
#define REPORT_COUNT(size) (0x94 | size) //bytes
|
||||||
|
#define PUSH(size) (0xa4 | size)
|
||||||
|
#define POP(size) (0xb4 | size)
|
||||||
|
|
||||||
|
/* Local items */
|
||||||
|
#define USAGE(size) (0x08 | size)
|
||||||
|
#define USAGE_MINIMUM(size) (0x18 | size)
|
||||||
|
#define USAGE_MAXIMUM(size) (0x28 | size)
|
||||||
|
#define DESIGNATOR_INDEX(size) (0x38 | size)
|
||||||
|
#define DESIGNATOR_MINIMUM(size) (0x48 | size)
|
||||||
|
#define DESIGNATOR_MAXIMUM(size) (0x58 | size)
|
||||||
|
#define STRING_INDEX(size) (0x78 | size)
|
||||||
|
#define STRING_MINIMUM(size) (0x88 | size)
|
||||||
|
#define STRING_MAXIMUM(size) (0x98 | size)
|
||||||
|
#define DELIMITER(size) (0xa8 | size)
|
||||||
|
|
||||||
|
/* HID Report */
|
||||||
|
/* Where report IDs are used the first byte of 'data' will be the */
|
||||||
|
/* report ID and 'length' will include this report ID byte. */
|
||||||
|
|
||||||
|
#define MAX_HID_REPORT_SIZE (64)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t length;
|
||||||
|
uint8_t data[MAX_HID_REPORT_SIZE];
|
||||||
|
} HID_REPORT;
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef RADIO_GAMEPAD_H
|
||||||
|
#define RADIO_GAMEPAD_H
|
||||||
|
|
||||||
|
#include <SPI.h>
|
||||||
|
#include <nRF24L01.h>
|
||||||
|
#include <RF24.h>
|
||||||
|
|
||||||
|
#define GAMEPAD_REPORT_ARRAY_ADD 1
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
|
RF24 radio(7, 8); // CE, CSN
|
||||||
|
const byte address[13] = "OpenRetroPad";
|
||||||
|
|
||||||
|
class RadioGamepad : public AbstractGamepad {
|
||||||
|
public:
|
||||||
|
RadioGamepad() : AbstractGamepad() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void begin(void) {
|
||||||
|
Serial.println("RadioGamepad.begin");
|
||||||
|
radio.begin();
|
||||||
|
radio.openWritingPipe(address);
|
||||||
|
radio.setPALevel(RF24_PA_MIN);
|
||||||
|
radio.stopListening();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sync(const uint8_t cIdx) {
|
||||||
|
Serial.println("RadioGamepad.sync");
|
||||||
|
gamepadReport[15] = cIdx;
|
||||||
|
radio.write(&gamepadReport, 16);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef RadioGamepad Gamepad;
|
||||||
|
|
||||||
|
#endif // RADIO_GAMEPAD_H
|
|
@ -0,0 +1,131 @@
|
||||||
|
#ifndef USB_GAMEPAD_H
|
||||||
|
#define USB_GAMEPAD_H
|
||||||
|
|
||||||
|
#include <WString.h>
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
|
#include "HID.h"
|
||||||
|
|
||||||
|
// ATT: 20 chars max (including NULL at the end) according to Arduino source code.
|
||||||
|
// Additionally serial number is used to differentiate arduino projects to have different button maps!
|
||||||
|
const char* gp_serial = "SF30 Pro";
|
||||||
|
|
||||||
|
class Gamepad_ : public PluggableUSBModule {
|
||||||
|
private:
|
||||||
|
uint8_t reportId;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint8_t epType[1];
|
||||||
|
uint8_t protocol;
|
||||||
|
uint8_t idle;
|
||||||
|
|
||||||
|
/*
|
||||||
|
int getInterface(uint8_t* interfaceCount);
|
||||||
|
int getDescriptor(USBSetup& setup);
|
||||||
|
uint8_t getShortName(char *name);
|
||||||
|
bool setup(USBSetup& setup);
|
||||||
|
*/
|
||||||
|
|
||||||
|
int getInterface(uint8_t* interfaceCount) {
|
||||||
|
*interfaceCount += 1; // uses 1
|
||||||
|
HIDDescriptor hidInterface = {D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_NONE, HID_PROTOCOL_NONE),
|
||||||
|
D_HIDREPORT(sizeof(_hidReportDescriptor)),
|
||||||
|
D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01)};
|
||||||
|
return USB_SendControl(0, &hidInterface, sizeof(hidInterface));
|
||||||
|
}
|
||||||
|
|
||||||
|
int getDescriptor(USBSetup& setup) {
|
||||||
|
// Check if this is a HID Class Descriptor request
|
||||||
|
if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In a HID Class Descriptor wIndex cointains the interface number
|
||||||
|
if (setup.wIndex != pluggedInterface) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the protocol on reenumeration. Normally the host should not assume the state of the protocol
|
||||||
|
// due to the USB specs, but Windows and Linux just assumes its in report mode.
|
||||||
|
protocol = HID_REPORT_PROTOCOL;
|
||||||
|
|
||||||
|
return USB_SendControl(TRANSFER_PGM, _hidReportDescriptor, sizeof(_hidReportDescriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setup(USBSetup& setup) {
|
||||||
|
if (pluggedInterface != setup.wIndex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t request = setup.bRequest;
|
||||||
|
uint8_t requestType = setup.bmRequestType;
|
||||||
|
|
||||||
|
if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE) {
|
||||||
|
if (request == HID_GET_REPORT) {
|
||||||
|
// TODO: HID_GetReport();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (request == HID_GET_PROTOCOL) {
|
||||||
|
// TODO: Send8(protocol);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) {
|
||||||
|
if (request == HID_SET_PROTOCOL) {
|
||||||
|
protocol = setup.wValueL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (request == HID_SET_IDLE) {
|
||||||
|
idle = setup.wValueL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (request == HID_SET_REPORT) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t getShortName(char* name) {
|
||||||
|
if (!next) {
|
||||||
|
strcpy(name, gp_serial);
|
||||||
|
return strlen(name);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Gamepad_(void) : PluggableUSBModule(1, 1, epType), protocol(HID_REPORT_PROTOCOL), idle(1) {
|
||||||
|
epType[0] = EP_TYPE_INTERRUPT_IN;
|
||||||
|
PluggableUSB().plug(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send(const void* d, int len) {
|
||||||
|
USB_Send(pluggedEndpoint | TRANSFER_RELEASE, d, len);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class UsbGamepad : public AbstractGamepad {
|
||||||
|
public:
|
||||||
|
String deviceManufacturer;
|
||||||
|
String deviceName;
|
||||||
|
Gamepad_ gamepad[GAMEPAD_COUNT];
|
||||||
|
|
||||||
|
UsbGamepad(String deviceName = "8BitDo SN30 Pro+", String deviceManufacturer = "8Bitdo SF30 Pro") : AbstractGamepad() {
|
||||||
|
this->deviceName = deviceName;
|
||||||
|
this->deviceManufacturer = deviceManufacturer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sync(const uint8_t cIdx) {
|
||||||
|
gamepad[cIdx].send(&gamepadReport, GAMEPAD_REPORT_LEN);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef UsbGamepad Gamepad;
|
||||||
|
|
||||||
|
#endif // USB_GAMEPAD_H
|
|
@ -0,0 +1,245 @@
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#define ARDUINO_ARCH_ESP32 1
|
||||||
|
#include "HIDTypes.h"
|
||||||
|
|
||||||
|
#define BUTTON_A 1
|
||||||
|
#define BUTTON_B 2
|
||||||
|
#define BUTTON_MENU 4
|
||||||
|
#define BUTTON_X 8
|
||||||
|
#define BUTTON_Y 16
|
||||||
|
#define BUTTON_TL 64
|
||||||
|
#define BUTTON_L 64
|
||||||
|
#define BUTTON_TR 128
|
||||||
|
#define BUTTON_R 128
|
||||||
|
#define BUTTON_TL2 256
|
||||||
|
#define BUTTON_TR2 512
|
||||||
|
#define BUTTON_SELECT 1024
|
||||||
|
#define BUTTON_START 2048
|
||||||
|
#define BUTTON_THUMBL 8192
|
||||||
|
#define BUTTON_THUMBR 16384
|
||||||
|
|
||||||
|
#define BUTTON_1 1
|
||||||
|
#define BUTTON_2 2
|
||||||
|
#define BUTTON_3 4
|
||||||
|
#define BUTTON_4 8
|
||||||
|
#define BUTTON_5 16
|
||||||
|
#define BUTTON_6 32
|
||||||
|
#define BUTTON_7 64
|
||||||
|
#define BUTTON_8 128
|
||||||
|
#define BUTTON_9 256
|
||||||
|
#define BUTTON_10 512
|
||||||
|
#define BUTTON_11 1024
|
||||||
|
#define BUTTON_12 2048
|
||||||
|
#define BUTTON_13 4096
|
||||||
|
#define BUTTON_14 8192
|
||||||
|
#define BUTTON_15 16384
|
||||||
|
#define BUTTON_16 32768
|
||||||
|
#define BUTTON_17 65536
|
||||||
|
#define BUTTON_18 131072
|
||||||
|
#define BUTTON_19 262144
|
||||||
|
#define BUTTON_20 524288
|
||||||
|
#define BUTTON_21 1048576
|
||||||
|
#define BUTTON_22 2097152
|
||||||
|
#define BUTTON_23 4194304
|
||||||
|
#define BUTTON_24 8388608
|
||||||
|
#define BUTTON_25 16777216
|
||||||
|
#define BUTTON_26 33554432
|
||||||
|
#define BUTTON_27 67108864
|
||||||
|
#define BUTTON_28 134217728
|
||||||
|
#define BUTTON_29 268435456
|
||||||
|
#define BUTTON_30 536870912
|
||||||
|
#define BUTTON_31 1073741824
|
||||||
|
#define BUTTON_32 2147483648
|
||||||
|
|
||||||
|
#define DPAD_CENTER 0
|
||||||
|
#define DPAD_CENTERED 0
|
||||||
|
#define DPAD_UP 1
|
||||||
|
#define DPAD_UP_RIGHT 2
|
||||||
|
#define DPAD_RIGHT 3
|
||||||
|
#define DPAD_DOWN_RIGHT 4
|
||||||
|
#define DPAD_DOWN 5
|
||||||
|
#define DPAD_DOWN_LEFT 6
|
||||||
|
#define DPAD_LEFT 7
|
||||||
|
#define DPAD_UP_LEFT 8
|
||||||
|
|
||||||
|
#ifndef GAMEPAD_REPORT_ARRAY_ADD
|
||||||
|
// this is used by radio gamepad to send additional info
|
||||||
|
#define GAMEPAD_REPORT_ARRAY_ADD 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// this must match _hidReportDescriptor, length to actually send
|
||||||
|
#define GAMEPAD_REPORT_LEN 15
|
||||||
|
|
||||||
|
static const uint8_t _hidReportDescriptor[] PROGMEM = {
|
||||||
|
USAGE_PAGE(1),
|
||||||
|
0x01, // USAGE_PAGE (Generic Desktop)
|
||||||
|
USAGE(1),
|
||||||
|
0x05, // USAGE (Gamepad)
|
||||||
|
COLLECTION(1),
|
||||||
|
0x01, // COLLECTION (Application)
|
||||||
|
USAGE(1),
|
||||||
|
0x01, // USAGE (Pointer)
|
||||||
|
COLLECTION(1),
|
||||||
|
0x00, // COLLECTION (Physical)
|
||||||
|
|
||||||
|
// ------------------------------------------------- Buttons (1 to 32)
|
||||||
|
USAGE_PAGE(1),
|
||||||
|
0x09, // USAGE_PAGE (Button)
|
||||||
|
USAGE_MINIMUM(1),
|
||||||
|
0x01, // USAGE_MINIMUM (Button 1)
|
||||||
|
USAGE_MAXIMUM(1),
|
||||||
|
0x20, // USAGE_MAXIMUM (Button 32)
|
||||||
|
LOGICAL_MINIMUM(1),
|
||||||
|
0x00, // LOGICAL_MINIMUM (0)
|
||||||
|
LOGICAL_MAXIMUM(1),
|
||||||
|
0x01, // LOGICAL_MAXIMUM (1)
|
||||||
|
REPORT_SIZE(1),
|
||||||
|
0x01, // REPORT_SIZE (1)
|
||||||
|
REPORT_COUNT(1),
|
||||||
|
0x20, // REPORT_COUNT (32)
|
||||||
|
HIDINPUT(1),
|
||||||
|
0x02, // INPUT (Data, Variable, Absolute) ;32 button bits
|
||||||
|
// ------------------------------------------------- X/Y position, Z/rZ position
|
||||||
|
USAGE_PAGE(1),
|
||||||
|
0x01, // USAGE_PAGE (Generic Desktop)
|
||||||
|
COLLECTION(1),
|
||||||
|
0x00, // COLLECTION (Physical)
|
||||||
|
USAGE(1),
|
||||||
|
0x30, // USAGE (X)
|
||||||
|
USAGE(1),
|
||||||
|
0x31, // USAGE (Y)
|
||||||
|
USAGE(1),
|
||||||
|
0x32, // USAGE (Z)
|
||||||
|
USAGE(1),
|
||||||
|
0x35, // USAGE (rZ)
|
||||||
|
0x16,
|
||||||
|
0x01,
|
||||||
|
0x80, //LOGICAL_MINIMUM (-32767)
|
||||||
|
0x26,
|
||||||
|
0xFF,
|
||||||
|
0x7F, //LOGICAL_MAXIMUM (32767)
|
||||||
|
REPORT_SIZE(1),
|
||||||
|
0x10, // REPORT_SIZE (16)
|
||||||
|
REPORT_COUNT(1),
|
||||||
|
0x04, // REPORT_COUNT (4)
|
||||||
|
HIDINPUT(1),
|
||||||
|
0x02, // INPUT (Data,Var,Abs)
|
||||||
|
// ------------------------------------------------- Triggers
|
||||||
|
USAGE(1),
|
||||||
|
0x33, // USAGE (rX) Left Trigger
|
||||||
|
USAGE(1),
|
||||||
|
0x34, // USAGE (rY) Right Trigger
|
||||||
|
LOGICAL_MINIMUM(1),
|
||||||
|
0x81, // LOGICAL_MINIMUM (-127)
|
||||||
|
LOGICAL_MAXIMUM(1),
|
||||||
|
0x7f, // LOGICAL_MAXIMUM (127)
|
||||||
|
REPORT_SIZE(1),
|
||||||
|
0x08, // REPORT_SIZE (8)
|
||||||
|
REPORT_COUNT(1),
|
||||||
|
0x02, // REPORT_COUNT (2)
|
||||||
|
HIDINPUT(1),
|
||||||
|
0x02, // INPUT (Data, Variable, Absolute) ;4 bytes (X,Y,Z,rZ)
|
||||||
|
END_COLLECTION(0), // END_COLLECTION
|
||||||
|
|
||||||
|
USAGE_PAGE(1),
|
||||||
|
0x01, // USAGE_PAGE (Generic Desktop)
|
||||||
|
USAGE(1),
|
||||||
|
0x39, // USAGE (Hat switch)
|
||||||
|
USAGE(1),
|
||||||
|
0x39, // USAGE (Hat switch)
|
||||||
|
LOGICAL_MINIMUM(1),
|
||||||
|
0x01, // LOGICAL_MINIMUM (1)
|
||||||
|
LOGICAL_MAXIMUM(1),
|
||||||
|
0x08, // LOGICAL_MAXIMUM (8)
|
||||||
|
REPORT_SIZE(1),
|
||||||
|
0x04, // REPORT_SIZE (4)
|
||||||
|
REPORT_COUNT(1),
|
||||||
|
0x02, // REPORT_COUNT (2)
|
||||||
|
HIDINPUT(1),
|
||||||
|
0x02, // INPUT (Data, Variable, Absolute) ;1 byte Hat1, Hat2
|
||||||
|
|
||||||
|
END_COLLECTION(0), // END_COLLECTION
|
||||||
|
END_COLLECTION(0) // END_COLLECTION
|
||||||
|
};
|
||||||
|
|
||||||
|
class AbstractGamepad {
|
||||||
|
public:
|
||||||
|
uint32_t _buttons[GAMEPAD_COUNT];
|
||||||
|
uint8_t gamepadReport[GAMEPAD_REPORT_LEN + GAMEPAD_REPORT_ARRAY_ADD];
|
||||||
|
|
||||||
|
AbstractGamepad() {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void begin(void) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void end(void) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool isConnected(void) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void setAxes(const uint8_t cIdx, int16_t x, int16_t y, int16_t z, int16_t rZ, char rX, char rY, signed char hat) {
|
||||||
|
if (x == -32768) {
|
||||||
|
x = -32767;
|
||||||
|
}
|
||||||
|
if (y == -32768) {
|
||||||
|
y = -32767;
|
||||||
|
}
|
||||||
|
if (z == -32768) {
|
||||||
|
z = -32767;
|
||||||
|
}
|
||||||
|
if (rZ == -32768) {
|
||||||
|
rZ = -32767;
|
||||||
|
}
|
||||||
|
|
||||||
|
gamepadReport[0] = _buttons[cIdx];
|
||||||
|
gamepadReport[1] = (_buttons[cIdx] >> 8);
|
||||||
|
gamepadReport[2] = (_buttons[cIdx] >> 16);
|
||||||
|
gamepadReport[3] = (_buttons[cIdx] >> 24);
|
||||||
|
gamepadReport[4] = x;
|
||||||
|
gamepadReport[5] = (x >> 8);
|
||||||
|
gamepadReport[6] = y;
|
||||||
|
gamepadReport[7] = (y >> 8);
|
||||||
|
gamepadReport[8] = z;
|
||||||
|
gamepadReport[9] = (z >> 8);
|
||||||
|
gamepadReport[10] = rZ;
|
||||||
|
gamepadReport[11] = (rZ >> 8);
|
||||||
|
gamepadReport[12] = (signed char)(rX - 128);
|
||||||
|
gamepadReport[13] = (signed char)(rY - 128);
|
||||||
|
gamepadReport[14] = hat;
|
||||||
|
if (gamepadReport[12] == -128) {
|
||||||
|
gamepadReport[12] = -127;
|
||||||
|
}
|
||||||
|
if (gamepadReport[13] == -128) {
|
||||||
|
gamepadReport[13] = -127;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->sync(cIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void setHatSync(const uint8_t cIdx, signed char hat) {
|
||||||
|
setAxes(cIdx, 0, 0, 0, 0, 0, 0, hat);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void buttons(const uint8_t cIdx, uint32_t b) {
|
||||||
|
_buttons[cIdx] = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void press(const uint8_t cIdx, uint32_t b) {
|
||||||
|
buttons(cIdx, _buttons[cIdx] | b);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void release(const uint8_t cIdx, uint32_t b) {
|
||||||
|
buttons(cIdx, _buttons[cIdx] & ~b);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool isPressed(const uint8_t cIdx, uint32_t b) {
|
||||||
|
return ((b & _buttons[cIdx]) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void sync(const uint8_t cIdx); // actually sends report
|
||||||
|
};
|
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
This directory is intended for PlatformIO Unit Testing and project tests.
|
||||||
|
|
||||||
|
Unit Testing is a software testing method by which individual units of
|
||||||
|
source code, sets of one or more MCU program modules together with associated
|
||||||
|
control data, usage procedures, and operating procedures, are tested to
|
||||||
|
determine whether they are fit for use. Unit testing finds problems early
|
||||||
|
in the development cycle.
|
||||||
|
|
||||||
|
More information about PlatformIO Unit Testing:
|
||||||
|
- https://docs.platformio.org/page/plus/unit-testing.html
|
Loading…
Reference in New Issue