68 Commits

Author SHA1 Message Date
wiredopposite
ea14d683ad add blueretro support via i2c 2025-03-31 17:40:46 -06:00
wiredopposite
8ef8d8daf6 Add Blueretro support, fix latency bug 2025-03-31 17:04:50 -06:00
wiredopposite
62a22d26de Create FUNDING.yml 2025-01-21 15:47:27 -07:00
wiredopposite
658eb1dd39 update to webapp master 2025-01-21 15:41:05 -07:00
wiredopposite
0693a873b7 change webapp branch 2025-01-21 15:35:48 -07:00
wiredopposite
007cd7fb72 fix overflow issue when converting 10bit to 16bit range 2025-01-21 15:28:03 -07:00
wiredopposite
90a931a118 update webapp 2025-01-21 12:47:27 -07:00
wiredopposite
43bb4408af fix hardware timers interrupting flash writes 2025-01-20 11:50:17 -07:00
wiredopposite
12654e351c add deadzone settings 2025-01-20 10:11:14 -07:00
wiredopposite
9cf3f0cbb0 v1.0.0-alpha3 2025-01-08 23:16:01 -07:00
wiredopposite
380bbe7cf7 v1.0.0-alpha3 2025-01-08 23:03:26 -07:00
wiredopposite
b3bcbff50a v1.0.0-alpha3 2025-01-08 22:52:58 -07:00
wiredopposite
7cb01051c3 remove std::random_device 2024-12-31 21:54:23 -07:00
wiredopposite
c4ebea1d01 decouple pico-sdk from device drivers 2024-12-31 21:13:58 -07:00
wiredopposite
de3f25d183 Add Pico+ESP32 diagram 2024-12-31 18:32:42 -07:00
wiredopposite
e4269d441d Change ESP32 BT reset pin number 2024-12-31 17:48:31 -07:00
wiredopposite
10203c814b Update readme, add horipad ids 2024-12-31 17:26:13 -07:00
wiredopposite
404997686f Fix XInput send_report + cleanup esp32 i2c logic 2024-12-31 14:10:53 -07:00
wiredopposite
8e53aa5d6b Update readme 2024-12-20 09:51:44 -07:00
wiredopposite
5599f2d834 Move webapp to separate repo and update readme 2024-12-20 09:48:51 -07:00
wiredopposite
6ed1026d72 Move webapp to separate repo and update readme 2024-12-20 09:47:26 -07:00
wiredopposite
9d8d5dfc63 Move webapp to separate repo 2024-12-20 09:41:59 -07:00
wiredopposite
ae2777172d v1.0.0alpha 2024-12-20 01:26:31 -07:00
wiredopposite
ee8d5e7036 v1.0.0-alpha 2024-12-20 00:32:43 -07:00
wiredopposite
9a8a5313b2 v1.0.0-alpha 2024-12-19 23:37:57 -07:00
wiredopposite
49edbb62e6 v1.0.0-alpha 2024-12-19 23:30:50 -07:00
wiredopposite
a5bfa4f9d2 v1.0.0-alpha 2024-12-19 23:19:13 -07:00
wiredopposite
ddd1e912c9 v1.0.0-alpha 2024-12-19 23:18:24 -07:00
wiredopposite
aabb00f2b5 v1.0.0-alpha 2024-12-19 23:15:27 -07:00
wiredopposite
f863f84976 v1.0.0-alpha 2024-12-13 18:58:18 -07:00
wiredopposite
da14684b90 v1.0.0-alpha 2024-12-08 23:53:50 -07:00
wiredopposite
0608838767 v1.0.0-alpha 2024-12-08 23:50:24 -07:00
wiredopposite
0d9d586767 v1.0.0-alpha 2024-12-08 23:03:38 -07:00
wiredopposite
ea69e607cb v1.0.0-alpha 2024-12-08 16:32:05 -07:00
wiredopposite
3eb0b56080 v1.0.0-alpha 2024-12-08 16:28:56 -07:00
wiredopposite
a87ed08a63 Update issue templates 2024-04-19 22:19:40 -06:00
wiredopposite
e3acca6d5c add PowerA Enhanced controller 2024-04-19 17:07:27 -06:00
wiredopposite
2c601723f1 update readme 2024-04-17 21:54:00 -06:00
wiredopposite
d6dcbc52a7 update readme 2024-04-17 21:42:59 -06:00
wiredopposite
e210b50c1a update readme 2024-04-17 21:42:05 -06:00
wiredopposite
ae59b5e0c9 update readme 2024-04-17 21:38:43 -06:00
wiredopposite
9ebbbc3735 update readme 2024-04-17 21:27:06 -06:00
wiredopposite
f3ddd49252 xinput host 2024-04-17 20:12:05 -06:00
wiredopposite
f7da9a07d0 xinput 2024-04-17 20:08:04 -06:00
wiredopposite
e2ececd2e0 restrict input mode for 2+ players 2024-04-17 20:01:14 -06:00
wiredopposite
3604c8e112 fixed wireless adapter rumble 2024-04-17 19:40:56 -06:00
wiredopposite
86c60d76a4 switch to tusb_xinput fork 2024-04-17 19:16:17 -06:00
wiredopposite
2613221d44 new boards 2024-04-17 18:26:02 -06:00
wiredopposite
1f27c3d4b5 resolve dinput bug 2024-04-16 18:28:16 -06:00
wiredopposite
da72c91c04 resolve dinput bug 2024-04-16 18:27:10 -06:00
wiredopposite
933206398a resolve dinput bug 2024-04-16 18:25:24 -06:00
wiredopposite
0a053f8f3d modularizing device drivers 2024-04-16 18:13:13 -06:00
wiredopposite
b1535c9c75 refactoring for different hardware 2024-04-16 15:19:57 -06:00
wiredopposite
383274c83b fixed dinput bug 2024-04-16 15:09:23 -06:00
wiredopposite
54bbdf0a41 4 player support 2024-03-24 20:53:41 -06:00
wiredopposite
b0fd734194 add config header file 2024-03-24 10:10:26 -06:00
wiredopposite
49fff842f2 host side refactor 2024-03-24 10:06:05 -06:00
wiredopposite
3995fd538c fixed sys reset/tusb conflict 2024-03-11 20:47:18 -06:00
wiredopposite
2643628dac input mode switching 2024-03-11 14:23:13 -06:00
wiredopposite
8fb77437cf input mode selection 2024-03-11 14:18:01 -06:00
wiredopposite
559633010e typo 2024-03-05 19:51:58 -07:00
wiredopposite
323706929b ps3 support and cdc mode 2024-03-05 19:28:39 -07:00
wiredopposite
ff48a17750 ps3 support and cdc mode 2024-03-05 19:04:38 -07:00
wiredopposite
20fd72e86f refactor and more controllers 2024-03-04 16:27:53 -07:00
wiredopposite
2be0455d6f refactor and more controllers 2024-03-01 16:53:07 -07:00
wiredopposite
e519b337fd update license 2024-02-21 15:57:22 -07:00
wiredopposite
51ab32fe99 Create LICENSE 2024-02-20 18:25:47 -07:00
wiredopposite
8edb2a272c initial 2024-02-20 18:04:24 -07:00
271 changed files with 25499 additions and 4471 deletions

View File

@@ -1,212 +0,0 @@
---
Language: Cpp
# BasedOnStyle: Google
AccessModifierOffset: -1
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveMacros: true
AlignConsecutiveAssignments: true
AlignConsecutiveBitFields: true
AlignConsecutiveDeclarations: true
AlignEscapedNewlines: Left
AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
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
BreakBeforeConceptDeclarations: true
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 160
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: true
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*\.h>'
Priority: 1
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 3
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '([-_](test|unittest))?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseLabels: true
IndentCaseBlocks: true
IndentGotoLabels: true
IndentPPDirectives: None
IndentExternBlock: AfterExternBlock
IndentRequires: false
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
LambdaBodyIndentation: Signature
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
PenaltyIndentedWhitespace: 0
PointerAlignment: Left
PPIndentWidth: -1
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: pb
BasedOnStyle: google
ReferenceAlignment: Pointer
ReflowComments: true
ShortNamespaceLines: 1
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: true
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceAroundPointerQualifiers: Default
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: Never
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
BitFieldColonSpacing: Both
Standard: Auto
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseCRLF: false
UseTab: Never
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
- NS_SWIFT_NAME
- CF_SWIFT_NAME
...

15
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: wiredopposite
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
thanks_dev: # Replace with a single thanks.dev username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

28
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,28 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**What type of controller do you have the issue with?**
Please include the model, even better to include the PID/VID.
You can get the PID and VID like this:
- plug the controller into your computer
- open Device Manager
- right click the controller (probably listed under Human Interface Devices)
- select Properties
- go to the Details tab
- select Hardware IDs from the dropdown menu
It will look like this: HID\VID_046D&PID_C05A
**What platform are you using the OGX-Mini on?**
OG Xbox, PS3, Switch, etc.
**What board are you using?**
Pi Pico, Adafruit Feather, RP2040-Zero...

View File

@@ -0,0 +1,17 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Additional context**
Add any other context or screenshots about the feature request here.

20
.gitignore vendored
View File

@@ -1,7 +1,13 @@
build
.vscode
release
generated
tools
.ignore
src/usbh/tusb_hid/experiment
Firmware/.vscode
Firmware/RP2040/build
Firmware/RP2040/.ignore
Firmware/RP2040/src/USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid_xremote_rom.h
Firmware/RP2040/src/BLEServer/att_delayed_response.h
Firmware/ESP32/main/BLEServer/att_delayed_response.h
Firmware/ESP32/.ignore
Firmware/ESP32/build
Firmware/ESP32/components/btstack
Firmware/ESP32/sdkconfig.old
Firmware/external/pico-sdk
Firmware/external/picotool
Tools/dvd-dongle-rom.bin

21
.gitmodules vendored Normal file
View File

@@ -0,0 +1,21 @@
[submodule "Firmware/external/bluepad32"]
path = Firmware/external/bluepad32
url = https://github.com/ricardoquesada/bluepad32.git
[submodule "Firmware/external/tinyusb"]
path = Firmware/external/tinyusb
url = https://github.com/hathach/tinyusb.git
[submodule "Tools/dump-dvd-kit"]
path = Tools/dump-dvd-kit
url = https://github.com/XboxDev/dump-dvd-kit.git
[submodule "Firmware/external/Pico-PIO-USB"]
path = Firmware/external/Pico-PIO-USB
url = https://github.com/wiredopposite/Pico-PIO-USB.git
[submodule "WebApp"]
path = WebApp
url = https://github.com/wiredopposite/OGX-Mini-WebApp.git
[submodule "Firmware/external/libfixmath"]
path = Firmware/external/libfixmath
url = https://github.com/PetteriAimonen/libfixmath.git
[submodule "Firmware/ESP32_Blueretro"]
path = Firmware/ESP32_Blueretro
url = https://github.com/wiredopposite/BlueRetro.git

20
.vscode/c_cpp_properties.json vendored Normal file
View File

@@ -0,0 +1,20 @@
{
"configurations": [
{
"name": "Pico",
"includePath": [
"${workspaceFolder}/Firmware/external/pico-sdk/**"
],
"forcedInclude": [
"${workspaceFolder}/Firmware/external/pico-sdk/src/common/pico_base_headers/include/pico.h",
"${workspaceFolder}/Firmware/RP2040/build/generated/pico_base/pico/config_autogen.h"
],
"defines": [],
"compileCommands": "${workspaceFolder}/Firmware/RP2040/build/compile_commands.json",
"cStandard": "c17",
"cppStandard": "c++14",
"intelliSenseMode": "linux-gcc-arm"
}
],
"version": 4
}

64
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,64 @@
{
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"cmake.sourceDirectory": "${workspaceFolder}/Firmware/RP2040",
"cmake.buildDirectory": "${workspaceFolder}/Firmware/RP2040/build",
"cmake.configureArgs": [
"-DOGXM_BOARD=EXTERNAL_4CH_I2C",
"-DMAX_GAMEPADS=4"
],
"files.associations": {
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"*.tcc": "cpp",
"cctype": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"concepts": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"list": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"optional": "cpp",
"random": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"initializer_list": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"cinttypes": "cpp",
"typeinfo": "cpp",
"xmemory": "cpp",
"xtr1common": "cpp",
"xutility": "cpp"
},
}

View File

@@ -1,99 +0,0 @@
cmake_minimum_required(VERSION 3.12)
message("Build type: \"${CMAKE_BUILD_TYPE}\"")
# Project name
set(NAME OGX-Mini)
# Board type
set(PICO_BOARD none)
# Fixes that allow some MCH2022 badges with a slowly starting oscillator to boot properly
add_compile_definitions(PICO_BOOT_STAGE2_CHOOSE_GENERIC_03H=1 PICO_XOSC_STARTUP_DELAY_MULTIPLIER=64)
# SDK
include($ENV{PICO_SDK_PATH}/pico_sdk_init.cmake)
project(${NAME} C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
if (PICO_SDK_VERSION_STRING VERSION_LESS "1.3.0")
message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.3.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
endif()
set(PICO_PIO_USB_PATH "${CMAKE_CURRENT_LIST_DIR}/lib/Pico-PIO-USB")
set(PICO_TINYUSB_PATH "${CMAKE_CURRENT_LIST_DIR}/lib/tinyusb")
pico_sdk_init()
add_subdirectory(lib/Pico-PIO-USB)
file(GLOB_RECURSE SOURCES
"src/usbh/*"
"src/usbh/tusb_xinput/*"
"src/usbh/tusb_hid/*"
"src/*"
"src/usbd/*"
"src/usbd/drivers/*"
"src/usbd/drivers/astro/*"
"src/usbd/drivers/egret/*"
"src/usbd/drivers/hid/*"
"src/usbd/drivers/keyboard/*"
"src/usbd/drivers/mdmini/*"
"src/usbd/drivers/neogeo/*"
"src/usbd/drivers/net/*"
"src/usbd/drivers/pcengine/*"
"src/usbd/drivers/ps4/*"
"src/usbd/drivers/psclassic/*"
"src/usbd/drivers/shared/*"
"src/usbd/drivers/switch/*"
"src/usbd/drivers/xbone/*"
"src/usbd/drivers/xboxog/*"
"src/usbd/drivers/xinput/*")
# Firmware
add_executable(${NAME}
${SOURCES}
)
target_include_directories(${NAME} PUBLIC
${CMAKE_CURRENT_LIST_DIR}/src ${CMAKE_CURRENT_LIST_DIR}/lib)
include_directories(
lib/
src/usbh
src/usbd
src/usbd/drivers)
#------- Comment out the following line if compiling for the normal Pi Pico -------#
add_compile_definitions(FEATHER_RP2040)
#------- USB host data +/- will be GPIO 0/1 for the Pico --------------------------#
#------ These are your options for platforms -----#
#------ Uncomment only one at a time -------------#
add_compile_definitions(HOST_ORIGINAL_XBOX)
# add_compile_definitions(HOST_XINPUT)
# add_compile_definitions(HOST_NINTENDO_SWITCH)
target_link_libraries(${NAME}
pico_stdlib
pico_unique_id
pico_multicore
hardware_watchdog
hardware_flash
hardware_sync
hardware_uart
hardware_pio
hardware_pwm
hardware_adc
hardware_i2c
tinyusb_device
tinyusb_board
tinyusb_host
tinyusb_pico_pio_usb
cmsis_core
)
pico_add_extra_outputs(${NAME})

View File

@@ -0,0 +1,23 @@
{
"configurations": [
{
"name": "ESP-IDF",
"compilerPath": "${default}",
"compileCommands": "${config:idf.buildPath}/compile_commands.json",
"includePath": [
"${config:idf.espIdfPath}/components/**",
"${config:idf.espIdfPathWin}/components/**",
"${workspaceFolder}/main/**"
],
"browse": {
"path": [
"${config:idf.espIdfPath}/components",
"${config:idf.espIdfPathWin}/components",
"${workspaceFolder}/main"
],
"limitSymbolsToIncludedHeaders": true
}
}
],
"version": 4
}

106
Firmware/ESP32/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,106 @@
{
"idf.adapterTargetName": "esp32",
"cmake.configureOnOpen": false,
"files.associations": {
"freertos.h": "c",
"stdint.h": "c",
"stdlib.h": "c",
"atomic": "cpp",
"array": "cpp",
"task.h": "c",
"bit": "cpp",
"*.tcc": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"complex": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"list": "cpp",
"map": "cpp",
"set": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"netfwd": "cpp",
"numeric": "cpp",
"random": "cpp",
"ratio": "cpp",
"regex": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"fstream": "cpp",
"future": "cpp",
"initializer_list": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"semaphore": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"thread": "cpp",
"cinttypes": "cpp",
"typeinfo": "cpp",
"c_wrapper.h": "c",
"queue": "cpp",
"charconv": "cpp",
"format": "cpp",
"forward_list": "cpp",
"iomanip": "cpp",
"ios": "cpp",
"locale": "cpp",
"optional": "cpp",
"ranges": "cpp",
"span": "cpp",
"stack": "cpp",
"xfacet": "cpp",
"xhash": "cpp",
"xiosbase": "cpp",
"xlocale": "cpp",
"xlocbuf": "cpp",
"xlocinfo": "cpp",
"xlocmes": "cpp",
"xlocmon": "cpp",
"xlocnum": "cpp",
"xloctime": "cpp",
"xmemory": "cpp",
"xstring": "cpp",
"xtr1common": "cpp",
"xtree": "cpp",
"xutility": "cpp"
},
"idf.portWin": "COM26",
"idf.flashType": "UART",
"C_Cpp.intelliSenseEngine": "default",
"idf.espIdfPathWin": "C:\\Programming\\esp\\v5.1\\esp-idf",
"idf.toolsPathWin": "C:\\Programming\\.espressif"
}

View File

@@ -0,0 +1,40 @@
cmake_minimum_required(VERSION 3.5)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(EXTERNAL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../external)
set(EXTERNAL_CMAKE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../cmake)
set(BTSTACK_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/components/btstack)
include(${CMAKE_CURRENT_SOURCE_DIR}/../FWDefines.cmake)
include(${EXTERNAL_CMAKE_DIR}/init_submodules.cmake)
include(${EXTERNAL_CMAKE_DIR}/patch_libs.cmake)
include(${EXTERNAL_CMAKE_DIR}/generate_gatt_header.cmake)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/integrate_btstack.cmake)
init_git_submodules(${EXTERNAL_DIR}
${EXTERNAL_DIR}/bluepad32
${EXTERNAL_DIR}/libfixmath
)
apply_lib_patches(${EXTERNAL_DIR})
integrate_btstack(${EXTERNAL_DIR})
generate_gatt_header(
${BTSTACK_ROOT}
${CMAKE_CURRENT_SOURCE_DIR}/../RP2040/src/BLEServer/att_delayed_response.gatt
${CMAKE_CURRENT_SOURCE_DIR}/main/BLEServer/att_delayed_response.h
)
set(BLUEPAD32_ROOT ${EXTERNAL_DIR}/bluepad32/src/components/bluepad32)
if(NOT EXISTS ${BLUEPAD32_ROOT})
message(FATAL_ERROR "External directory not found: ${BLUEPAD32_ROOT}")
endif()
set(EXTRA_COMPONENT_DIRS
${BLUEPAD32_ROOT}/..
${CMAKE_CURRENT_SOURCE_DIR}/components
)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project("${FW_NAME}-${FW_VERSION}-ESP32")

View File

@@ -0,0 +1,50 @@
function(integrate_btstack EXTERNAL_DIR)
if(NOT EXISTS ${EXTERNAL_DIR})
message(FATAL_ERROR "External directory not found: ${EXTERNAL_DIR}")
else()
message(STATUS "External directory found: ${EXTERNAL_DIR}")
endif()
set(PROJECT_COMPONENTS ${CMAKE_CURRENT_LIST_DIR}/components)
set(BTSTACK_DIR ${EXTERNAL_DIR}/bluepad32/external/btstack)
set(PROJECT_BTSTACK ${PROJECT_COMPONENTS}/btstack)
if (NOT EXISTS ${PROJECT_COMPONENTS})
message(STATUS "Creating components folder at ${PROJECT_COMPONENTS}")
file(MAKE_DIRECTORY ${PROJECT_COMPONENTS})
endif()
if (NOT EXISTS ${BTSTACK_DIR})
message(FATAL_ERROR "Error: No BTstack folder at ${BTSTACK_DIR}")
endif()
if (NOT EXISTS ${PROJECT_BTSTACK})
find_package(Python3 COMPONENTS Interpreter REQUIRED)
if(Python3_Interpreter_FOUND)
message(STATUS "Python3 found: ${Python3_EXECUTABLE}")
message(STATUS "Python3 version: ${Python3_VERSION}")
else()
message(FATAL_ERROR "Python3 not found! Python is required to integrate BTStack with ESP-IDF")
endif()
set(BTSTACK_SCRIPT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/components/integrate_btstack.py)
message(STATUS "Integrating BTStack with project")
execute_process(
COMMAND ${Python3_EXECUTABLE} ${BTSTACK_SCRIPT_PATH} ${BTSTACK_DIR} ${PROJECT_COMPONENTS}
WORKING_DIRECTORY ${BTSTACK_SCRIPT_PATH}/..
RESULT_VARIABLE PYTHON_RESULT
OUTPUT_VARIABLE PYTHON_OUTPUT
ERROR_VARIABLE PYTHON_ERROR
)
if(PYTHON_RESULT EQUAL 0)
message(STATUS "BTStack integration successful:\n${PYTHON_OUTPUT}")
else()
message(FATAL_ERROR "BTStack integration failed:\n${PYTHON_ERROR}")
endif()
endif()
message(STATUS "BTstack setup complete")
endfunction()

View File

@@ -0,0 +1,77 @@
# This script copies BTstack ESP32 port files to this project's components folder, ESP-IDF freaks out otherwise
import os
import sys
import shutil
def main(btstack_root, components_dir):
PROJECT_COMPONENTS = os.path.abspath(components_dir)
BTSTACK_DIR = os.path.abspath(btstack_root)
BTSTACK_PORT_DIR = os.path.abspath(os.path.join(BTSTACK_DIR, 'port', 'esp32'))
if not os.path.exists(PROJECT_COMPONENTS):
print(f"Error: No components folder at {PROJECT_COMPONENTS}")
sys.exit(10)
if not os.path.exists(BTSTACK_DIR):
print(f"Error: No BTstack folder at {BTSTACK_DIR}")
sys.exit(10)
PROJECT_BTSTACK = os.path.join(PROJECT_COMPONENTS, "btstack")
if os.path.exists(PROJECT_BTSTACK):
print("Deleting old BTstack component %s" % PROJECT_BTSTACK)
shutil.rmtree(PROJECT_BTSTACK)
# create components/btstack
print("Creating BTstack component at %s" % PROJECT_COMPONENTS)
shutil.copytree(os.path.join(BTSTACK_PORT_DIR, 'components', 'btstack'), PROJECT_BTSTACK)
dirs_to_copy = [
'src',
'3rd-party/bluedroid',
'3rd-party/hxcmod-player',
'3rd-party/lwip/dhcp-server',
'3rd-party/lc3-google',
'3rd-party/md5',
'3rd-party/micro-ecc',
'3rd-party/yxml',
'platform/freertos',
'platform/lwip',
'tool'
]
for dir in dirs_to_copy:
print('- %s' % dir)
shutil.copytree(os.path.join(BTSTACK_PORT_DIR, '..', '..', dir), os.path.join(PROJECT_BTSTACK, dir))
# manually prepare platform/embedded
print('- platform/embedded')
platform_embedded_path = PROJECT_BTSTACK + '/platform/embedded'
os.makedirs(platform_embedded_path)
platform_embedded_files_to_copy = [
'hal_time_ms.h',
'hal_uart_dma.h',
'hci_dump_embedded_stdout.h',
'hci_dump_embedded_stdout.c',
]
for file in platform_embedded_files_to_copy:
shutil.copy(os.path.join(BTSTACK_PORT_DIR, '..', '..', 'platform', 'embedded', file), platform_embedded_path)
if __name__ == "__main__":
if len(sys.argv) < 3:
print("Usage: script.py <path_to_btstack> <path_to_project_components>")
sys.exit(1)
btstack_root = sys.argv[1]
project_components = sys.argv[2]
if not os.path.exists(btstack_root):
print(f"Error: Specified external directory does not exist: {btstack_root}")
sys.exit(2)
if not os.path.exists(project_components):
print(f"Error: Specified components directory does not exist: {project_components}")
sys.exit(3)
main(btstack_root, project_components)

View File

@@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 3.5)
set(LIBFIXMATH_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../../external/libfixmath)
if (NOT EXISTS ${LIBFIXMATH_ROOT})
message(FATAL_ERROR "External directory not found: ${LIBFIXMATH_ROOT}")
else()
message(STATUS "Found libfixmath at ${LIBFIXMATH_ROOT}")
endif()
file(GLOB SRCS ${LIBFIXMATH_ROOT}/libfixmath/*.c)
idf_component_register(
SRCS
${SRCS}
INCLUDE_DIRS
${LIBFIXMATH_ROOT}
${LIBFIXMATH_ROOT}/libfixmath
)
target_compile_definitions(${COMPONENT_LIB} PRIVATE
FIXMATH_FAST_SIN
FIXMATH_NO_64BIT
FIXMATH_NO_CACHE
FIXMATH_NO_HARD_DIVISION
FIXMATH_NO_OVERFLOW
)

View File

@@ -0,0 +1,10 @@
dependencies:
idf:
source:
type: idf
version: 5.1.5
direct_dependencies:
- idf
manifest_hash: ed38c883314ee552977d073f5c9d0e2ab9acc3248eefbeb203f3439de3e31b6e
target: esp32
version: 2.0.0

View File

@@ -0,0 +1,334 @@
#include <cstring>
#include <string>
#include <esp_ota_ops.h>
#include <esp_system.h>
#include "att_server.h"
#include "btstack.h"
#include "BTManager/BTManager.h"
#include "Gamepad/Gamepad.h"
#include "BLEServer/BLEServer.h"
#include "BLEServer/att_delayed_response.h"
#include "UserSettings/UserProfile.h"
#include "UserSettings/UserSettings.h"
namespace BLEServer {
constexpr uint16_t PACKET_LEN_MAX = 20;
constexpr size_t GAMEPAD_LEN = 23;
namespace Handle
{
constexpr uint16_t FW_VERSION = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789020_01_VALUE_HANDLE;
constexpr uint16_t FW_NAME = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789021_01_VALUE_HANDLE;
constexpr uint16_t SETUP_READ = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789030_01_VALUE_HANDLE;
constexpr uint16_t SETUP_WRITE = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789031_01_VALUE_HANDLE;
constexpr uint16_t GET_SETUP = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789032_01_VALUE_HANDLE;
constexpr uint16_t PROFILE = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789040_01_VALUE_HANDLE;
constexpr uint16_t GAMEPAD = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789050_01_VALUE_HANDLE;
}
namespace ADV
{
// Flags general discoverable, BR/EDR not supported
static const uint8_t FLAGS[] = { 0x02, 0x01, 0x06 };
static const uint8_t NAME_TYPE = 0x09;
#pragma pack(push, 1)
struct Data
{
uint8_t flags[sizeof(FLAGS)];
uint8_t name_len;
uint8_t name_type;
uint8_t name[sizeof(FIRMWARE_NAME) - 1];
Data()
{
std::memcpy(flags, FLAGS, sizeof(flags));
name_len = sizeof(FIRMWARE_NAME);
name_type = NAME_TYPE;
std::string fw_name = FIRMWARE_NAME;
std::memcpy(name, fw_name.c_str(), std::min(sizeof(name), fw_name.size()));
}
};
static_assert(sizeof(Data) == 5 + sizeof(FIRMWARE_NAME) - 1, "BLEServer::ADV::Data struct size mismatch");
#pragma pack(pop)
}
#pragma pack(push, 1)
struct SetupPacket
{
DeviceDriverType device_type{DeviceDriverType::NONE};
uint8_t max_gamepads{MAX_GAMEPADS};
uint8_t player_idx{0};
uint8_t profile_id{0};
};
static_assert(sizeof(SetupPacket) == 4, "BLEServer::SetupPacket struct size mismatch");
#pragma pack(pop)
class ProfileReader
{
public:
ProfileReader() = default;
~ProfileReader() = default;
void set_setup_packet(const SetupPacket& setup_packet)
{
setup_packet_ = setup_packet;
current_offset_ = 0;
}
const SetupPacket& get_setup_packet() const
{
return setup_packet_;
}
uint16_t get_xfer_len()
{
return static_cast<uint16_t>(std::min(static_cast<size_t>(PACKET_LEN_MAX), sizeof(UserProfile) - current_offset_));
}
uint16_t get_profile_data(uint8_t* buffer, uint16_t buffer_len)
{
size_t copy_len = get_xfer_len();
if (!buffer || buffer_len < copy_len)
{
return 0;
}
if (current_offset_ == 0 && !set_profile())
{
return 0;
}
std::memcpy(buffer, reinterpret_cast<uint8_t*>(&profile_) + current_offset_, copy_len);
current_offset_ += copy_len;
if (current_offset_ >= sizeof(UserProfile))
{
current_offset_ = 0;
OGXM_LOG("ProfileReader: Read complete for profile ID: %i\n", profile_.id);
}
return copy_len;
}
private:
SetupPacket setup_packet_;
UserProfile profile_;
size_t current_offset_ = 0;
bool set_profile()
{
if (setup_packet_.profile_id == 0xFF)
{
if (setup_packet_.player_idx >= UserSettings::MAX_PROFILES)
{
return false;
}
profile_ = UserSettings::get_instance().get_profile_by_index(setup_packet_.player_idx);
OGXM_LOG("ProfileReader: Reading profile for player %d\n", setup_packet_.player_idx);
}
else
{
if (setup_packet_.profile_id > UserSettings::MAX_PROFILES)
{
return false;
}
profile_ = UserSettings::get_instance().get_profile_by_id(setup_packet_.profile_id);
OGXM_LOG("ProfileReader: Reading profile with ID %d\n", setup_packet_.profile_id);
}
return true;
}
};
class ProfileWriter
{
public:
ProfileWriter() = default;
~ProfileWriter() = default;
void set_setup_packet(const SetupPacket& setup_packet)
{
setup_packet_ = setup_packet;
current_offset_ = 0;
}
const SetupPacket& get_setup_packet() const
{
return setup_packet_;
}
uint16_t get_xfer_len()
{
return static_cast<uint16_t>(std::min(static_cast<size_t>(PACKET_LEN_MAX), sizeof(UserProfile) - current_offset_));
}
size_t set_profile_data(const uint8_t* buffer, uint16_t buffer_len)
{
size_t copy_len = get_xfer_len();
if (!buffer || buffer_len < copy_len)
{
return 0;
}
std::memcpy(reinterpret_cast<uint8_t*>(&profile_) + current_offset_, buffer, copy_len);
current_offset_ += copy_len;
size_t ret = current_offset_;
if (current_offset_ >= sizeof(UserProfile))
{
current_offset_ = 0;
}
return ret;
}
bool commit_profile()
{
if (setup_packet_.device_type != DeviceDriverType::NONE)
{
UserSettings::get_instance().store_profile_and_driver_type(setup_packet_.device_type, setup_packet_.player_idx, profile_);
}
else
{
UserSettings::get_instance().store_profile(setup_packet_.player_idx, profile_);
}
return true;
}
private:
SetupPacket setup_packet_;
UserProfile profile_;
size_t current_offset_ = 0;
};
ProfileReader profile_reader_;
ProfileWriter profile_writer_;
static int verify_write(const uint16_t buffer_size, const uint16_t expected_size)
{
if (buffer_size != expected_size)
{
return ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH;
}
return 0;
}
static uint16_t att_read_callback( hci_con_handle_t connection_handle,
uint16_t att_handle,
uint16_t offset,
uint8_t *buffer,
uint16_t buffer_size)
{
std::string fw_version;
std::string fw_name;
switch (att_handle)
{
case Handle::FW_VERSION:
fw_version = FIRMWARE_VERSION;
if (buffer)
{
std::memcpy(buffer, reinterpret_cast<const uint8_t*>(fw_version.c_str()), fw_version.size());
}
return static_cast<uint16_t>(fw_version.size());
case Handle::FW_NAME:
fw_name = FIRMWARE_NAME;
if (buffer)
{
std::memcpy(buffer, reinterpret_cast<const uint8_t*>(fw_name.c_str()), fw_name.size());;
}
return static_cast<uint16_t>(fw_name.size());
case Handle::GET_SETUP:
if (buffer)
{
buffer[0] = static_cast<uint8_t>(UserSettings::get_instance().get_current_driver());
buffer[1] = MAX_GAMEPADS;
buffer[2] = 0;
buffer[3] = UserSettings::get_instance().get_active_profile_id(0);
}
return static_cast<uint16_t>(sizeof(SetupPacket));
case Handle::PROFILE:
if (buffer)
{
return profile_reader_.get_profile_data(buffer, buffer_size);
}
return profile_reader_.get_xfer_len();
case Handle::GAMEPAD:
if (buffer)
{
I2CDriver::PacketIn packet_in = BTManager::get_instance().get_packet_in(0);
std::memcpy(buffer, &packet_in.dpad, 13);
}
return static_cast<uint16_t>(13);
default:
break;
}
return 0;
}
static int att_write_callback( hci_con_handle_t connection_handle,
uint16_t att_handle,
uint16_t transaction_mode,
uint16_t offset,
uint8_t *buffer,
uint16_t buffer_size)
{
int ret = 0;
switch (att_handle)
{
case Handle::SETUP_READ:
if ((ret = verify_write(buffer_size, sizeof(SetupPacket))) != 0)
{
break;
}
profile_reader_.set_setup_packet(*reinterpret_cast<SetupPacket*>(buffer));
break;
case Handle::SETUP_WRITE:
if ((ret = verify_write(buffer_size, sizeof(SetupPacket))) != 0)
{
break;
}
profile_writer_.set_setup_packet(*reinterpret_cast<SetupPacket*>(buffer));
break;
case Handle::PROFILE:
if ((ret = verify_write(buffer_size, profile_writer_.get_xfer_len())) != 0)
{
break;
}
if (profile_writer_.set_profile_data(buffer, buffer_size) == sizeof(UserProfile))
{
profile_writer_.commit_profile();
}
break;
default:
break;
}
return ret;
}
void init_server()
{
att_server_init(profile_data, att_read_callback, att_write_callback);
uint16_t adv_int_min = 0x0030;
uint16_t adv_int_max = 0x0030;
uint8_t adv_type = 0;
bd_addr_t null_addr;
std::memset(null_addr, 0, sizeof(null_addr));
static ADV::Data adv_data = ADV::Data();
gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00);
gap_advertisements_set_data(static_cast<uint8_t>(sizeof(adv_data)), reinterpret_cast<uint8_t*>(&adv_data));
gap_advertisements_enable(1);
}
} // namespace BLEServer

View File

@@ -0,0 +1,13 @@
#ifndef BLE_SERVER_H
#define BLE_SERVER_H
#include <cstdint>
#include "Gamepad/Gamepad.h"
namespace BLEServer
{
void init_server();
}
#endif // BLE_SERVER_H

View File

@@ -0,0 +1,257 @@
#include "btstack_port_esp32.h"
#include "btstack_run_loop.h"
#include "btstack_stdio_esp32.h"
#include "uni.h"
#include "sdkconfig.h"
#include "Board/ogxm_log.h"
#include "Board/board_api.h"
#include "BTManager/BTManager.h"
#include "BLEServer/BLEServer.h"
void BTManager::run_task()
{
board_api::init_board();
UserSettings::get_instance().initialize_flash();
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i)
{
UserProfile profile = UserSettings::get_instance().get_profile_by_index(i);
devices_[i].mapper.set_profile(profile);
}
i2c_driver_.initialize_i2c(
static_cast<i2c_port_t>(CONFIG_I2C_PORT),
static_cast<gpio_num_t>(CONFIG_I2C_SDA_PIN),
static_cast<gpio_num_t>(CONFIG_I2C_SCL_PIN),
CONFIG_I2C_BAUDRATE
);
xTaskCreatePinnedToCore(
[](void* parameter)
{
get_instance().i2c_driver_.run_tasks();
},
"i2c",
2048 * 2,
nullptr,
configMAX_PRIORITIES-8,
nullptr,
1
);
btstack_init();
uni_platform_set_custom(get_bp32_driver());
uni_init(0, nullptr);
btstack_timer_source_t led_timer;
led_timer.process = check_led_cb;
led_timer.context = nullptr;
btstack_run_loop_set_timer(&led_timer, LED_TIME_MS);
btstack_run_loop_add_timer(&led_timer);
btstack_timer_source_t driver_update_timer;
driver_update_timer.process = driver_update_timer_cb;
driver_update_timer.context = nullptr;
btstack_run_loop_set_timer(&driver_update_timer, UserSettings::GP_CHECK_DELAY_MS);
btstack_run_loop_add_timer(&driver_update_timer);
BLEServer::init_server();
//Doesn't return
btstack_run_loop_execute();
}
bool BTManager::is_connected(uint8_t index)
{
if (index >= devices_.size())
{
return false;
}
return devices_[index].connected.load();
}
bool BTManager::any_connected()
{
for (auto& device : devices_)
{
if (device.connected.load())
{
return true;
}
}
return false;
}
void BTManager::check_led_cb(btstack_timer_source *ts)
{
static bool led_state = false;
led_state = !led_state;
if constexpr (board_api::NUM_LEDS == 1)
{
board_api::set_led(get_instance().any_connected() ? true : (led_state ? true : false));
}
else
{
for (uint8_t i = 0; i < board_api::NUM_LEDS; ++i)
{
board_api::set_led(i, get_instance().is_connected(i) ? true : (led_state ? true : false));
}
}
btstack_run_loop_set_timer(ts, LED_TIME_MS);
btstack_run_loop_add_timer(ts);
}
void BTManager::send_driver_type(DeviceDriverType driver_type)
{
if constexpr (I2CDriver::MULTI_SLAVE)
{
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i)
{
I2CDriver::PacketIn packet_in = devices_[i].packet_in;
packet_in.packet_id = I2CDriver::PacketID::SET_DRIVER;
packet_in.index = i;
packet_in.device_driver = driver_type;
i2c_driver_.write_packet(i + 1, packet_in);
}
}
else
{
I2CDriver::PacketIn packet_in = devices_[0].packet_in;
packet_in.packet_id = I2CDriver::PacketID::SET_DRIVER;
packet_in.index = 0;
packet_in.device_driver = driver_type;
i2c_driver_.write_packet(0x01, packet_in);
}
}
uni_hid_device_t* BTManager::get_connected_bp32_device(uint8_t index)
{
uni_hid_device_t* bp_device = nullptr;
if (!(bp_device = uni_hid_device_get_instance_for_idx(index)) ||
!uni_bt_conn_is_connected(&bp_device->conn) ||
!bp_device->report_parser.play_dual_rumble)
{
return nullptr;
}
return bp_device;
}
void BTManager::driver_update_timer_cb(btstack_timer_source *ts)
{
BTManager& bt_manager = get_instance();
I2CDriver::PacketIn& packet_in = bt_manager.devices_.front().packet_in;
if (get_connected_bp32_device(0) &&
UserSettings::get_instance().check_for_driver_change(packet_in))
{
OGXM_LOG("BP32: Driver change detected\n");
UserSettings::get_instance().store_driver_type(UserSettings::get_instance().get_current_driver());
}
//Notify pico of current driver type regardless of change
bt_manager.send_driver_type(UserSettings::get_instance().get_current_driver());
btstack_run_loop_set_timer(ts, UserSettings::GP_CHECK_DELAY_MS);
btstack_run_loop_add_timer(ts);
}
void BTManager::send_feedback_cb(void* context)
{
FBContext* fb_context = reinterpret_cast<FBContext*>(context);
uni_hid_device_t* bp_device = nullptr;
if (!(bp_device = get_connected_bp32_device(fb_context->index)))
{
return;
}
I2CDriver::PacketOut packet_out = fb_context->packet_out->load();
if (packet_out.rumble_l || packet_out.rumble_r)
{
bp_device->report_parser.play_dual_rumble(
bp_device,
0,
FEEDBACK_TIME_MS,
packet_out.rumble_l,
packet_out.rumble_r
);
}
}
//This will have to be changed once full support for multiple devices is added
void BTManager::feedback_timer_cb(btstack_timer_source *ts)
{
static FBContext fb_contexts[MAX_GAMEPADS];
BTManager& bt_manager = get_instance();
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i)
{
if (!get_connected_bp32_device(i))
{
continue;
}
FBContext& fb_context = fb_contexts[i];
fb_context.index = i;
fb_context.packet_out = &bt_manager.devices_[i].packet_out;
fb_context.cb_reg.callback = send_feedback_cb;
fb_context.cb_reg.context = reinterpret_cast<void*>(&fb_context);
//Register a read on i2c thread, with callback to send feedback on btstack thread
bt_manager.i2c_driver_.read_packet(I2CDriver::MULTI_SLAVE ? i + 1 : 0x01,
[&fb_context](const I2CDriver::PacketOut& packet_out)
{
fb_context.packet_out->store(packet_out);
btstack_run_loop_execute_on_main_thread(&fb_context.cb_reg);
});
}
btstack_run_loop_set_timer(ts, FEEDBACK_TIME_MS);
btstack_run_loop_add_timer(ts);
}
void BTManager::manage_connection(uint8_t index, bool connected)
{
devices_[index].connected.store(connected);
if (connected)
{
if (!fb_timer_running_)
{
fb_timer_running_ = true;
fb_timer_.process = feedback_timer_cb;
fb_timer_.context = nullptr;
btstack_run_loop_set_timer(&fb_timer_, FEEDBACK_TIME_MS);
btstack_run_loop_add_timer(&fb_timer_);
}
}
else
{
if (!any_connected())
{
if (fb_timer_running_)
{
fb_timer_running_ = false;
btstack_run_loop_remove_timer(&fb_timer_);
}
}
I2CDriver::PacketIn packet_in = I2CDriver::PacketIn();
packet_in.packet_id = I2CDriver::PacketID::SET_PAD;
packet_in.index = index;
i2c_driver_.write_packet(I2CDriver::MULTI_SLAVE ? packet_in.index + 1 : 0x01, packet_in);
}
}
I2CDriver::PacketIn BTManager::get_packet_in(uint8_t index)
{
if (index >= devices_.size())
{
return I2CDriver::PacketIn();
}
return devices_[index].packet_in;
}

View File

@@ -0,0 +1,83 @@
#ifndef _BT_MANAGER_H_
#define _BT_MANAGER_H_
#include <cstdint>
#include <array>
#include <atomic>
#include <string>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "I2CDriver/I2CDriver.h"
#include "Gamepad/Gamepad.h"
class BTManager
{
public:
static BTManager& get_instance()
{
static BTManager instance;
return instance;
}
void run_task();
bool any_connected();
bool is_connected(uint8_t index);
I2CDriver::PacketIn get_packet_in(uint8_t index);
private:
BTManager() = default;
~BTManager() = default;
BTManager(const BTManager&) = delete;
BTManager& operator=(const BTManager&) = delete;
static constexpr uint32_t FEEDBACK_TIME_MS = 200;
static constexpr uint32_t LED_TIME_MS = 500;
struct Device
{
std::atomic<bool> connected{false};
GamepadMapper mapper;
I2CDriver::PacketIn packet_in;
std::atomic<I2CDriver::PacketOut> packet_out; //Can be updated from i2c thread
};
struct FBContext
{
uint8_t index;
std::atomic<I2CDriver::PacketOut>* packet_out;
btstack_context_callback_registration_t cb_reg;
};
std::array<Device, MAX_GAMEPADS> devices_;
I2CDriver i2c_driver_;
btstack_timer_source_t fb_timer_;
bool fb_timer_running_ = false;
void send_driver_type(DeviceDriverType driver_type);
void manage_connection(uint8_t index, bool connected);
static uni_hid_device_t* get_connected_bp32_device(uint8_t index);
static void check_led_cb(btstack_timer_source *ts);
static void send_feedback_cb(void* context);
static void feedback_timer_cb(btstack_timer_source *ts);
static void driver_update_timer_cb(btstack_timer_source *ts);
//Bluepad32 driver
void init(int argc, const char** arg_V);
void init_complete_cb(void);
uni_error_t device_discovered_cb(bd_addr_t addr, const char* name, uint16_t cod, uint8_t rssi);
void device_connected_cb(uni_hid_device_t* device);
void device_disconnected_cb(uni_hid_device_t* device);
uni_error_t device_ready_cb(uni_hid_device_t* device);
void controller_data_cb(uni_hid_device_t* bp_device, uni_controller_t* controller);
const uni_property_t* get_property_cb(uni_property_idx_t idx);
void oob_event_cb(uni_platform_oob_event_t event, void* data);
static uni_platform* get_bp32_driver();
}; // class BTManager
#endif // _BT_MANAGER_H_

View File

@@ -0,0 +1,194 @@
#include "btstack_port_esp32.h"
#include "btstack_run_loop.h"
#include "btstack_stdio_esp32.h"
#include "uni.h"
#include "Board/ogxm_log.h"
#include "BTManager/BTManager.h"
#include "BLEServer/BLEServer.h"
void BTManager::init(int argc, const char** arg_V)
{
OGXM_LOG("BP32: Initializing Bluepad32\n");
}
void BTManager::init_complete_cb(void)
{
uni_bt_enable_new_connections_unsafe(true);
// uni_bt_del_keys_unsafe();
uni_property_dump_all();
}
uni_error_t BTManager::device_discovered_cb(bd_addr_t addr, const char* name, uint16_t cod, uint8_t rssi)
{
if (!((cod & UNI_BT_COD_MINOR_MASK) & UNI_BT_COD_MINOR_GAMEPAD))
{
return UNI_ERROR_IGNORE_DEVICE;
}
return UNI_ERROR_SUCCESS;
}
void BTManager::device_connected_cb(uni_hid_device_t* device)
{
OGXM_LOG("BP32: Device connected, addr: %p, index: %i\n", device, uni_hid_device_get_idx_for_instance(device));
}
void BTManager::device_disconnected_cb(uni_hid_device_t* device)
{
int idx = uni_hid_device_get_idx_for_instance(device);
if (idx >= MAX_GAMEPADS || idx < 0)
{
return;
}
manage_connection(static_cast<uint8_t>(idx), false);
}
uni_error_t BTManager::device_ready_cb(uni_hid_device_t* device)
{
int idx = uni_hid_device_get_idx_for_instance(device);
if (idx >= MAX_GAMEPADS || idx < 0)
{
return UNI_ERROR_IGNORE_DEVICE;
}
manage_connection(static_cast<uint8_t>(idx), true);
return UNI_ERROR_SUCCESS;
}
void BTManager::controller_data_cb(uni_hid_device_t* bp_device, uni_controller_t* controller)
{
static uni_gamepad_t prev_uni_gps[MAX_GAMEPADS] = {};
if (controller->klass != UNI_CONTROLLER_CLASS_GAMEPAD)
{
return;
}
int idx = uni_hid_device_get_idx_for_instance(bp_device);
uni_gamepad_t *uni_gp = &controller->gamepad;
if (idx < 0 || idx >= MAX_GAMEPADS || std::memcmp(uni_gp, &prev_uni_gps[idx], sizeof(uni_gamepad_t)) == 0)
{
return;
}
I2CDriver::PacketIn& packet_in = devices_[idx].packet_in;
GamepadMapper& mapper = devices_[idx].mapper;
packet_in = I2CDriver::PacketIn();
packet_in.packet_id = I2CDriver::PacketID::SET_PAD;
packet_in.index = static_cast<uint8_t>(idx);
switch (uni_gp->dpad)
{
case DPAD_UP:
packet_in.dpad = mapper.DPAD_UP;
break;
case DPAD_DOWN:
packet_in.dpad = mapper.DPAD_DOWN;
break;
case DPAD_LEFT:
packet_in.dpad = mapper.DPAD_LEFT;
break;
case DPAD_RIGHT:
packet_in.dpad = mapper.DPAD_RIGHT;
break;
case (DPAD_UP | DPAD_RIGHT):
packet_in.dpad = mapper.DPAD_UP_RIGHT;
break;
case (DPAD_DOWN | DPAD_RIGHT):
packet_in.dpad = mapper.DPAD_DOWN_RIGHT;
break;
case (DPAD_DOWN | DPAD_LEFT):
packet_in.dpad = mapper.DPAD_DOWN_LEFT;
break;
case (DPAD_UP | DPAD_LEFT):
packet_in.dpad = mapper.DPAD_UP_LEFT;
break;
default:
break;
}
if (uni_gp->buttons & BUTTON_A) packet_in.buttons |= mapper.BUTTON_A;
if (uni_gp->buttons & BUTTON_B) packet_in.buttons |= mapper.BUTTON_B;
if (uni_gp->buttons & BUTTON_X) packet_in.buttons |= mapper.BUTTON_X;
if (uni_gp->buttons & BUTTON_Y) packet_in.buttons |= mapper.BUTTON_Y;
if (uni_gp->buttons & BUTTON_SHOULDER_L) packet_in.buttons |= mapper.BUTTON_LB;
if (uni_gp->buttons & BUTTON_SHOULDER_R) packet_in.buttons |= mapper.BUTTON_RB;
if (uni_gp->buttons & BUTTON_THUMB_L) packet_in.buttons |= mapper.BUTTON_L3;
if (uni_gp->buttons & BUTTON_THUMB_R) packet_in.buttons |= mapper.BUTTON_R3;
if (uni_gp->misc_buttons & MISC_BUTTON_BACK) packet_in.buttons |= mapper.BUTTON_BACK;
if (uni_gp->misc_buttons & MISC_BUTTON_START) packet_in.buttons |= mapper.BUTTON_START;
if (uni_gp->misc_buttons & MISC_BUTTON_SYSTEM) packet_in.buttons |= mapper.BUTTON_SYS;
if (uni_gp->misc_buttons & MISC_BUTTON_CAPTURE) packet_in.buttons |= mapper.BUTTON_MISC;
packet_in.trigger_l = mapper.scale_trigger_l<10>(static_cast<uint16_t>(uni_gp->brake));
packet_in.trigger_r = mapper.scale_trigger_r<10>(static_cast<uint16_t>(uni_gp->throttle));
// auto joy_l = mapper.scale_joystick_l<10>(uni_gp->axis_x, uni_gp->axis_y);
// auto joy_r = mapper.scale_joystick_r<10>(uni_gp->axis_rx, uni_gp->axis_ry);
// packet_in.joystick_lx = joy_l.first;
// packet_in.joystick_ly = joy_l.second;
// packet_in.joystick_rx = joy_r.first;
// packet_in.joystick_ry = joy_r.second;
std::tie(packet_in.joystick_lx, packet_in.joystick_ly) = mapper.scale_joystick_l<10>(uni_gp->axis_x, uni_gp->axis_y);
std::tie(packet_in.joystick_rx, packet_in.joystick_ry) = mapper.scale_joystick_r<10>(uni_gp->axis_rx, uni_gp->axis_ry);
i2c_driver_.write_packet(I2CDriver::MULTI_SLAVE ? packet_in.index + 1 : 0x01, packet_in);
std::memcpy(&prev_uni_gps[idx], uni_gp, sizeof(uni_gamepad_t));
}
const uni_property_t* BTManager::get_property_cb(uni_property_idx_t idx)
{
return nullptr;
}
void BTManager::oob_event_cb(uni_platform_oob_event_t event, void* data)
{
return;
}
uni_platform* BTManager::get_bp32_driver()
{
static uni_platform driver = {
.name = "OGXMiniW",
.init =
[](int argc, const char** argv)
{ get_instance().init(argc, argv); },
.on_init_complete =
[](void)
{ get_instance().init_complete_cb(); },
.on_device_discovered =
[](bd_addr_t addr, const char* name, uint16_t cod, uint8_t rssi)
{ return get_instance().device_discovered_cb(addr, name, cod, rssi); },
.on_device_connected =
[](uni_hid_device_t* device)
{ get_instance().device_connected_cb(device); },
.on_device_disconnected =
[](uni_hid_device_t* device)
{ get_instance().device_disconnected_cb(device); },
.on_device_ready =
[](uni_hid_device_t* device)
{ return get_instance().device_ready_cb(device); },
.on_controller_data =
[](uni_hid_device_t* device, uni_controller_t* controller)
{ get_instance().controller_data_cb(device, controller); },
.get_property =
[](uni_property_idx_t idx)
{ return get_instance().get_property_cb(idx); },
.on_oob_event =
[](uni_platform_oob_event_t event, void* data)
{ get_instance().oob_event_cb(event, data); },
};
return &driver;
}

View File

@@ -0,0 +1,76 @@
#include <driver/gpio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "sdkconfig.h"
#include "board/board_api.h"
namespace board_api {
SemaphoreHandle_t leds_mutex_ = nullptr;
// SemaphoreHandle_t reset_mutex_ = nullptr;
void init_board()
{
if (leds_mutex_ == nullptr)
{
leds_mutex_ = xSemaphoreCreateMutex();
}
// if (reset_mutex_ == nullptr)
// {
// reset_mutex_ = xSemaphoreCreateMutex();
// }
if (xSemaphoreTake(leds_mutex_, portMAX_DELAY))
{
for (const auto& LED_PIN : LED_PINS)
{
gpio_reset_pin(LED_PIN);
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
gpio_set_level(LED_PIN, 0);
}
xSemaphoreGive(leds_mutex_);
}
// if (xSemaphoreTake(reset_mutex_, portMAX_DELAY))
// {
// gpio_reset_pin(RESET_PIN);
// gpio_set_direction(RESET_PIN, GPIO_MODE_INPUT);
// gpio_set_level(RESET_PIN, 1);
// xSemaphoreGive(reset_mutex_);
// }
}
//Set LED by index
void set_led(uint8_t index, bool state)
{
if constexpr (NUM_LEDS < 1)
{
return;
}
if (index >= NUM_LEDS)
{
return;
}
if (xSemaphoreTake(leds_mutex_, portMAX_DELAY))
{
gpio_set_level(LED_PINS[index], state ? 1 : 0);
xSemaphoreGive(leds_mutex_);
}
}
//Set first LED
void set_led(bool state)
{
if constexpr (NUM_LEDS < 1)
{
return;
}
if (xSemaphoreTake(leds_mutex_, portMAX_DELAY))
{
gpio_set_level(LED_PINS[0], state ? 1 : 0);
xSemaphoreGive(leds_mutex_);
}
}
} // namespace board_api

View File

@@ -0,0 +1,43 @@
#ifndef _BOARD_API_H_
#define _BOARD_API_H_
#include <cstdint>
#include <driver/gpio.h>
#include "sdkconfig.h"
#define MAX_GPIO_NUM 40
namespace board_api
{
static constexpr gpio_num_t RESET_PIN = static_cast<gpio_num_t>(CONFIG_RESET_PIN);
static_assert(RESET_PIN < MAX_GPIO_NUM, "Invalid RESET_PIN");
static constexpr gpio_num_t LED_PINS[] =
{
#if defined(CONFIG_ENABLE_LED_1) && (CONFIG_LED_PIN_1 < MAX_GPIO_NUM) && (CONFIG_BLUEPAD32_MAX_DEVICES > 0)
static_cast<gpio_num_t>(CONFIG_LED_PIN_1),
#if defined(CONFIG_ENABLE_LED_2) && (CONFIG_LED_PIN_2 < MAX_GPIO_NUM) && (CONFIG_BLUEPAD32_MAX_DEVICES > 1)
static_cast<gpio_num_t>(CONFIG_LED_PIN_2),
#if defined(CONFIG_ENABLE_LED_3) && (CONFIG_LED_PIN_3 < MAX_GPIO_NUM) && (CONFIG_BLUEPAD32_MAX_DEVICES > 2)
static_cast<gpio_num_t>(CONFIG_LED_PIN_3),
#if defined(CONFIG_ENABLE_LED_4) && (CONFIG_LED_PIN_4 < MAX_GPIO_NUM) && (CONFIG_BLUEPAD32_MAX_DEVICES > 3)
static_cast<gpio_num_t>(CONFIG_LED_PIN_4),
#endif // CONFIG_ENABLE_LED_4
#endif // CONFIG_ENABLE_LED_3
#endif // CONFIG_ENABLE_LED_2
#endif // CONFIG_ENABLE_LED_1
};
static constexpr uint8_t NUM_LEDS = sizeof(LED_PINS) / sizeof(LED_PINS[0]);
void init_board();
void set_led(uint8_t index, bool state);
void set_led(bool state);
}
#endif // _BOARD_API_H_

View File

@@ -0,0 +1,67 @@
#ifndef _OGXM_LOG_H_
#define _OGXM_LOG_H_
#include <esp_log.h>
#if ESP_LOG_LEVEL >= ESP_LOG_INFO
#include <cstdint>
#include <string>
#include <sstream>
#include <iomanip>
#include <stdarg.h>
#include "uni.h"
namespace OGXM
{
static inline void log(const std::string& message)
{
logi(message.c_str());
}
static inline void log(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
uni_logv(fmt, args);
va_end(args);
}
static inline void log_hex(const std::string& message, const uint8_t* data, const size_t len)
{
log(message);
std::ostringstream hex_stream;
hex_stream << std::hex << std::setfill('0');
int char_num = 0;
for (uint16_t i = 0; i < len; ++i)
{
hex_stream << std::setw(2) << static_cast<int>(data[i]) << " ";
char_num++;
if (char_num == 16)
{
hex_stream << "\n";
char_num = 0;
}
}
if (char_num != 0)
{
hex_stream << "\n";
}
log(hex_stream.str());
}
} // namespace OGXM
#define OGXM_LOG OGXM::log
#define OGXM_LOG_HEX OGXM::log_hex
#else // ESP_LOG_LEVEL >= ESP_LOG_INFO
#define OGXM_LOG(...)
#define OGXM_LOG_HEX(...)
#endif // ESP_LOG_LEVEL >= ESP_LOG_INFO
#endif // _OGXM_LOG_H_

View File

@@ -0,0 +1,29 @@
include(${CMAKE_CURRENT_SOURCE_DIR}/../../FWDefines.cmake)
idf_component_register(
SRCS
"main.c"
"main.cpp"
"Board/board_api.cpp"
"BTManager/BTManager.cpp"
"BTManager/BTManager_BP32.cpp"
"BLEServer/BLEServer.cpp"
"I2CDriver/I2CDriver.cpp"
"UserSettings/UserSettings.cpp"
"UserSettings/UserProfile.cpp"
"UserSettings/TriggerSettings.cpp"
"UserSettings/JoystickSettings.cpp"
INCLUDE_DIRS
"."
REQUIRES
bluepad32
btstack
driver
nvs_flash
libfixmath
)
target_compile_definitions(${COMPONENT_LIB} PRIVATE
FIRMWARE_NAME=\"${FW_NAME}\"
FIRMWARE_VERSION=\"${FW_VERSION}\"
)

View File

@@ -0,0 +1,450 @@
#ifndef GAMEPAD_H
#define GAMEPAD_H
#include <cstdint>
#include "sdkconfig.h"
#include "Gamepad/Range.h"
#include "Gamepad/fix16ext.h"
#include "UserSettings/UserProfile.h"
#include "UserSettings/UserSettings.h"
#include "Board/ogxm_log.h"
#define MAX_GAMEPADS CONFIG_BLUEPAD32_MAX_DEVICES
static_assert( MAX_GAMEPADS > 0 &&
MAX_GAMEPADS <= 4,
"MAX_GAMEPADS must be between 1 and 4");
namespace Gamepad
{
static constexpr uint8_t DPAD_UP = 0x01;
static constexpr uint8_t DPAD_DOWN = 0x02;
static constexpr uint8_t DPAD_LEFT = 0x04;
static constexpr uint8_t DPAD_RIGHT = 0x08;
static constexpr uint8_t DPAD_UP_LEFT = DPAD_UP | DPAD_LEFT;
static constexpr uint8_t DPAD_UP_RIGHT = DPAD_UP | DPAD_RIGHT;
static constexpr uint8_t DPAD_DOWN_LEFT = DPAD_DOWN | DPAD_LEFT;
static constexpr uint8_t DPAD_DOWN_RIGHT = DPAD_DOWN | DPAD_RIGHT;
static constexpr uint8_t DPAD_NONE = 0x00;
static constexpr uint16_t BUTTON_A = 0x0001;
static constexpr uint16_t BUTTON_B = 0x0002;
static constexpr uint16_t BUTTON_X = 0x0004;
static constexpr uint16_t BUTTON_Y = 0x0008;
static constexpr uint16_t BUTTON_L3 = 0x0010;
static constexpr uint16_t BUTTON_R3 = 0x0020;
static constexpr uint16_t BUTTON_BACK = 0x0040;
static constexpr uint16_t BUTTON_START = 0x0080;
static constexpr uint16_t BUTTON_LB = 0x0100;
static constexpr uint16_t BUTTON_RB = 0x0200;
static constexpr uint16_t BUTTON_SYS = 0x0400;
static constexpr uint16_t BUTTON_MISC = 0x0800;
static constexpr uint8_t ANALOG_OFF_UP = 0;
static constexpr uint8_t ANALOG_OFF_DOWN = 1;
static constexpr uint8_t ANALOG_OFF_LEFT = 2;
static constexpr uint8_t ANALOG_OFF_RIGHT = 3;
static constexpr uint8_t ANALOG_OFF_A = 4;
static constexpr uint8_t ANALOG_OFF_B = 5;
static constexpr uint8_t ANALOG_OFF_X = 6;
static constexpr uint8_t ANALOG_OFF_Y = 7;
static constexpr uint8_t ANALOG_OFF_LB = 8;
static constexpr uint8_t ANALOG_OFF_RB = 9;
}
class GamepadMapper
{
public:
uint8_t DPAD_UP = Gamepad::DPAD_UP ;
uint8_t DPAD_DOWN = Gamepad::DPAD_DOWN ;
uint8_t DPAD_LEFT = Gamepad::DPAD_LEFT ;
uint8_t DPAD_RIGHT = Gamepad::DPAD_RIGHT ;
uint8_t DPAD_UP_LEFT = Gamepad::DPAD_UP_LEFT ;
uint8_t DPAD_UP_RIGHT = Gamepad::DPAD_UP_RIGHT ;
uint8_t DPAD_DOWN_LEFT = Gamepad::DPAD_DOWN_LEFT ;
uint8_t DPAD_DOWN_RIGHT = Gamepad::DPAD_DOWN_RIGHT;
uint8_t DPAD_NONE = Gamepad::DPAD_NONE ;
uint16_t BUTTON_A = Gamepad::BUTTON_A ;
uint16_t BUTTON_B = Gamepad::BUTTON_B ;
uint16_t BUTTON_X = Gamepad::BUTTON_X ;
uint16_t BUTTON_Y = Gamepad::BUTTON_Y ;
uint16_t BUTTON_L3 = Gamepad::BUTTON_L3 ;
uint16_t BUTTON_R3 = Gamepad::BUTTON_R3 ;
uint16_t BUTTON_BACK = Gamepad::BUTTON_BACK ;
uint16_t BUTTON_START = Gamepad::BUTTON_START;
uint16_t BUTTON_LB = Gamepad::BUTTON_LB ;
uint16_t BUTTON_RB = Gamepad::BUTTON_RB ;
uint16_t BUTTON_SYS = Gamepad::BUTTON_SYS ;
uint16_t BUTTON_MISC = Gamepad::BUTTON_MISC ;
GamepadMapper() = default;
~GamepadMapper() = default;
void set_profile(const UserProfile& profile)
{
set_profile_settings(profile);
set_profile_mappings(profile);
}
template <uint8_t bits = 0, typename T>
inline std::pair<int16_t, int16_t> scale_joystick_r(T x, T y, bool invert_y = false) const
{
int16_t joy_x = 0;
int16_t joy_y = 0;
if constexpr (bits > 0)
{
joy_x = Range::scale_from_bits<int16_t, bits>(x);
joy_y = Range::scale_from_bits<int16_t, bits>(y);
}
else if constexpr (!std::is_same_v<T, int16_t>)
{
joy_x = Range::scale<int16_t>(x);
joy_y = Range::scale<int16_t>(y);
}
else
{
joy_x = x;
joy_y = y;
}
return joy_settings_r_en_
? apply_joystick_settings(joy_x, joy_y, joy_settings_r_, invert_y)
: std::make_pair(joy_x, (invert_y ? Range::invert(joy_y) : joy_y));
}
template <uint8_t bits = 0, typename T>
inline std::pair<int16_t, int16_t> scale_joystick_l(T x, T y, bool invert_y = false) const
{
int16_t joy_x = 0;
int16_t joy_y = 0;
if constexpr (bits > 0)
{
joy_x = Range::scale_from_bits<int16_t, bits>(x);
joy_y = Range::scale_from_bits<int16_t, bits>(y);
}
else if constexpr (!std::is_same_v<T, int16_t>)
{
joy_x = Range::scale<int16_t>(x);
joy_y = Range::scale<int16_t>(y);
}
else
{
joy_x = x;
joy_y = y;
}
return joy_settings_l_en_
? apply_joystick_settings(joy_x, joy_y, joy_settings_l_, invert_y)
: std::make_pair(joy_x, (invert_y ? Range::invert(joy_y) : joy_y));
}
template <uint8_t bits = 0, typename T>
inline uint8_t scale_trigger_l(T value) const
{
uint8_t trigger_value = 0;
if constexpr (bits > 0)
{
trigger_value = Range::scale_from_bits<uint8_t, bits>(value);
}
else if constexpr (!std::is_same_v<T, uint8_t>)
{
trigger_value = Range::scale<uint8_t>(value);
}
else
{
trigger_value = value;
}
return trig_settings_l_en_
? apply_trigger_settings(trigger_value, trig_settings_l_)
: trigger_value;
}
template <uint8_t bits = 0, typename T>
inline uint8_t scale_trigger_r(T value) const
{
uint8_t trigger_value = 0;
if constexpr (bits > 0)
{
trigger_value = Range::scale_from_bits<uint8_t, bits>(value);
}
else if constexpr (!std::is_same_v<T, uint8_t>)
{
trigger_value = Range::scale<uint8_t>(value);
}
else
{
trigger_value = value;
}
return trig_settings_r_en_
? apply_trigger_settings(trigger_value, trig_settings_r_)
: trigger_value;
}
private:
JoystickSettings joy_settings_l_;
JoystickSettings joy_settings_r_;
TriggerSettings trig_settings_l_;
TriggerSettings trig_settings_r_;
bool joy_settings_l_en_{false};
bool joy_settings_r_en_{false};
bool trig_settings_l_en_{false};
bool trig_settings_r_en_{false};
void set_profile_settings(const UserProfile& profile)
{
if ((joy_settings_l_en_ = !joy_settings_l_.is_same(profile.joystick_settings_l)))
{
joy_settings_l_.set_from_raw(profile.joystick_settings_l);
//This needs to be addressed in the webapp, just multiply here for now
joy_settings_l_.axis_restrict *= static_cast<int16_t>(100);
joy_settings_l_.angle_restrict *= static_cast<int16_t>(100);
joy_settings_l_.anti_dz_angular *= static_cast<int16_t>(100);
}
if ((joy_settings_r_en_ = !joy_settings_r_.is_same(profile.joystick_settings_r)))
{
joy_settings_r_.set_from_raw(profile.joystick_settings_r);
//This needs to be addressed in the webapp, just multiply here for now
joy_settings_r_.axis_restrict *= static_cast<int16_t>(100);
joy_settings_r_.angle_restrict *= static_cast<int16_t>(100);
joy_settings_r_.anti_dz_angular *= static_cast<int16_t>(100);
}
if ((trig_settings_l_en_ = !trig_settings_l_.is_same(profile.trigger_settings_l)))
{
trig_settings_l_.set_from_raw(profile.trigger_settings_l);
}
if ((trig_settings_r_en_ = !trig_settings_r_.is_same(profile.trigger_settings_r)))
{
trig_settings_r_.set_from_raw(profile.trigger_settings_r);
}
OGXM_LOG("GamepadMapper: JoyL: %s, JoyR: %s, TrigL: %s, TrigR: %s\n",
joy_settings_l_en_ ? "Enabled" : "Disabled",
joy_settings_r_en_ ? "Enabled" : "Disabled",
trig_settings_l_en_ ? "Enabled" : "Disabled",
trig_settings_r_en_ ? "Enabled" : "Disabled");
}
void set_profile_mappings(const UserProfile& profile)
{
DPAD_UP = profile.dpad_up;
DPAD_DOWN = profile.dpad_down;
DPAD_LEFT = profile.dpad_left;
DPAD_RIGHT = profile.dpad_right;
DPAD_UP_LEFT = profile.dpad_up | profile.dpad_left;
DPAD_UP_RIGHT = profile.dpad_up | profile.dpad_right;
DPAD_DOWN_LEFT = profile.dpad_down | profile.dpad_left;
DPAD_DOWN_RIGHT = profile.dpad_down | profile.dpad_right;
DPAD_NONE = 0;
BUTTON_A = profile.button_a;
BUTTON_B = profile.button_b;
BUTTON_X = profile.button_x;
BUTTON_Y = profile.button_y;
BUTTON_L3 = profile.button_l3;
BUTTON_R3 = profile.button_r3;
BUTTON_BACK = profile.button_back;
BUTTON_START = profile.button_start;
BUTTON_LB = profile.button_lb;
BUTTON_RB = profile.button_rb;
BUTTON_SYS = profile.button_sys;
BUTTON_MISC = profile.button_misc;
}
static inline std::pair<int16_t, int16_t> apply_joystick_settings(
int16_t gp_joy_x,
int16_t gp_joy_y,
const JoystickSettings& set,
bool invert_y)
{
static const Fix16
FIX_0(0.0f),
FIX_1(1.0f),
FIX_2(2.0f),
FIX_45(45.0f),
FIX_90(90.0f),
FIX_100(100.0f),
FIX_180(180.0f),
FIX_EPSILON(0.0001f),
FIX_EPSILON2(0.001f),
FIX_ELLIPSE_DEF(1.570796f),
FIX_DIAG_DIVISOR(0.29289f);
Fix16 x = (set.invert_x ? Fix16(Range::invert(gp_joy_x)) : Fix16(gp_joy_x)) / Range::MAX<int16_t>;
Fix16 y = ((set.invert_y ^ invert_y) ? Fix16(Range::invert(gp_joy_y)) : Fix16(gp_joy_y)) / Range::MAX<int16_t>;
const Fix16 abs_x = fix16::abs(x);
const Fix16 abs_y = fix16::abs(y);
const Fix16 inv_axis_restrict = FIX_1 / (FIX_1 - set.axis_restrict);
Fix16 rAngle = (abs_x < FIX_EPSILON)
? FIX_90
: fix16::rad2deg(fix16::abs(fix16::atan(y / x)));
Fix16 axial_x = (abs_x <= set.axis_restrict && rAngle > FIX_45)
? FIX_0
: ((abs_x - set.axis_restrict) * inv_axis_restrict);
Fix16 axial_y = (abs_y <= set.axis_restrict && rAngle <= FIX_45)
? FIX_0
: ((abs_y - set.axis_restrict) * inv_axis_restrict);
Fix16 in_magnitude = fix16::sqrt(fix16::sq(axial_x) + fix16::sq(axial_y));
if (in_magnitude < set.dz_inner)
{
return { 0, 0 };
}
Fix16 angle =
fix16::abs(axial_x) < FIX_EPSILON
? FIX_90
: fix16::rad2deg(fix16::abs(fix16::atan(axial_y / axial_x)));
Fix16 anti_r_scale = (set.anti_dz_square_y_scale == FIX_0) ? set.anti_dz_square : set.anti_dz_square_y_scale;
Fix16 anti_dz_c = set.anti_dz_circle;
if (anti_r_scale > FIX_0 && anti_dz_c > FIX_0)
{
Fix16 anti_ellip_scale = anti_ellip_scale / anti_dz_c;
Fix16 ellipse_angle = fix16::atan((FIX_1 / anti_ellip_scale) * fix16::tan(fix16::rad2deg(rAngle)));
ellipse_angle = (ellipse_angle < FIX_0) ? FIX_ELLIPSE_DEF : ellipse_angle;
Fix16 ellipse_x = fix16::cos(ellipse_angle);
Fix16 ellipse_y = fix16::sqrt(fix16::sq(anti_ellip_scale) * (FIX_1 - fix16::sq(ellipse_x)));
anti_dz_c *= fix16::sqrt(fix16::sq(ellipse_x) + fix16::sq(ellipse_y));
}
if (anti_dz_c > FIX_0)
{
anti_dz_c = anti_dz_c / ((anti_dz_c * (FIX_1 - set.anti_dz_circle / set.dz_outer)) / (anti_dz_c * (FIX_1 - set.anti_dz_square)));
}
if (abs_x > set.axis_restrict && abs_y > set.axis_restrict)
{
const Fix16 FIX_ANGLE_MAX = set.angle_restrict / 2.0f;
if (angle > FIX_0 && angle < FIX_ANGLE_MAX)
{
angle = FIX_0;
}
if (angle > (FIX_90 - FIX_ANGLE_MAX))
{
angle = FIX_90;
}
if (angle > FIX_ANGLE_MAX && angle < (FIX_90 - FIX_ANGLE_MAX))
{
angle = ((angle - FIX_ANGLE_MAX) * FIX_90) / ((FIX_90 - FIX_ANGLE_MAX) - FIX_ANGLE_MAX);
}
}
Fix16 ref_angle = (angle < FIX_EPSILON2) ? FIX_0 : angle;
Fix16 diagonal = (angle > FIX_45) ? (((angle - FIX_45) * (-FIX_45)) / FIX_45) + FIX_45 : angle;
const Fix16 angle_comp = set.angle_restrict / FIX_2;
if (angle < FIX_90 && angle > FIX_0)
{
angle = ((angle * ((FIX_90 - angle_comp) - angle_comp)) / FIX_90) + angle_comp;
}
if (axial_x < FIX_0 && axial_y > FIX_0)
{
angle = -angle;
}
if (axial_x > FIX_0 && axial_y < FIX_0)
{
angle = angle - FIX_180;
}
if (axial_x < FIX_0 && axial_y < FIX_0)
{
angle = angle + FIX_180;
}
//Deadzone Warp
Fix16 out_magnitude = (in_magnitude - set.dz_inner) / (set.anti_dz_outer - set.dz_inner);
out_magnitude = fix16::pow(out_magnitude, (FIX_1 / set.curve)) * (set.dz_outer - anti_dz_c) + anti_dz_c;
out_magnitude = (out_magnitude > set.dz_outer && !set.uncap_radius) ? set.dz_outer : out_magnitude;
Fix16 d_scale = (((out_magnitude - anti_dz_c) * (set.diag_scale_max - set.diag_scale_min)) / (set.dz_outer - anti_dz_c)) + set.diag_scale_min;
Fix16 c_scale = (diagonal * (FIX_1 / fix16::sqrt(FIX_2))) / FIX_45; //Both these lines scale the intensity of the warping
c_scale = FIX_1 - fix16::sqrt(FIX_1 - c_scale * c_scale); //based on a circular curve to the perfect diagonal
d_scale = (c_scale * (d_scale - FIX_1)) / FIX_DIAG_DIVISOR + FIX_1;
out_magnitude = out_magnitude * d_scale;
//Scaling values for square antideadzone
Fix16 new_x = fix16::cos(fix16::deg2rad(angle)) * out_magnitude;
Fix16 new_y = fix16::sin(fix16::deg2rad(angle)) * out_magnitude;
//Magic angle wobble fix by user ME.
// if (angle > 45.0 && angle < 225.0) {
// newX = inv(Math.sin(deg2rad(angle - 90.0)))*outputMagnitude;
// newY = inv(Math.cos(deg2rad(angle - 270.0)))*outputMagnitude;
// }
//Square antideadzone scaling
Fix16 output_x = fix16::abs(new_x) * (FIX_1 - set.anti_dz_square / set.dz_outer) + set.anti_dz_square;
if (x < FIX_0)
{
output_x = -output_x;
}
if (ref_angle == FIX_90)
{
output_x = FIX_0;
}
Fix16 output_y = fix16::abs(new_y) * (FIX_1 - anti_r_scale / set.dz_outer) + anti_r_scale;
if (y < FIX_0)
{
output_y = -output_y;
}
if (ref_angle == FIX_0)
{
output_y = FIX_0;
}
output_x = fix16::clamp(output_x, -FIX_1, FIX_1) * Range::MAX<int16_t>;
output_y = fix16::clamp(output_y, -FIX_1, FIX_1) * Range::MAX<int16_t>;
return { static_cast<int16_t>(fix16_to_int(output_x)), static_cast<int16_t>(fix16_to_int(output_y)) };
}
static inline uint8_t apply_trigger_settings(uint8_t value, const TriggerSettings& set)
{
Fix16 abs_value = fix16::abs(Fix16(static_cast<int16_t>(value)) / static_cast<int16_t>(Range::MAX<uint8_t>));
if (abs_value < set.dz_inner)
{
return 0;
}
static const Fix16
FIX_0(0.0f),
FIX_1(1.0f),
FIX_2(2.0f);
Fix16 value_out = (abs_value - set.dz_inner) / (set.anti_dz_outer - set.dz_inner);
value_out = fix16::clamp(value_out, FIX_0, FIX_1);
if (set.anti_dz_inner > FIX_0)
{
value_out = set.anti_dz_inner + (FIX_1 - set.anti_dz_inner) * value_out;
}
if (set.curve != FIX_1)
{
value_out = fix16::pow(value_out, FIX_1 / set.curve);
}
if (set.anti_dz_outer < FIX_1)
{
value_out = fix16::clamp(value_out * (FIX_1 / (FIX_1 - set.anti_dz_outer)), FIX_0, FIX_1);
}
value_out *= set.dz_outer;
return static_cast<uint8_t>(fix16_to_int(value_out * static_cast<int16_t>(Range::MAX<uint8_t>)));
}
}; // class GamepadMapper
#endif // GAMEPAD_H

View File

@@ -0,0 +1,204 @@
#ifndef _RANGE_H_
#define _RANGE_H_
#include <cstdint>
#include <limits>
#include <type_traits>
#include "Board/ogxm_log.h"
namespace Range {
template <typename T>
requires std::is_integral_v<T>
constexpr T MAX = std::numeric_limits<T>::max();
template <typename T>
requires std::is_integral_v<T>
constexpr T MIN = std::numeric_limits<T>::min();
template <typename T>
requires std::is_integral_v<T>
constexpr T MID =
[] {
if constexpr (std::is_unsigned_v<T>)
{
return MAX<T> / 2 + 1;
}
return 0;
}();
static_assert(MID<uint8_t> == 128, "MID<uint8_t> != 128");
static_assert(MID<int8_t> == 0, "MID<int8_t> != 0");
template <typename T>
requires std::is_integral_v<T>
constexpr uint16_t NUM_BITS = sizeof(T) * 8;
//Maximum value for a given number of bits, result will be signed/unsigned depending on type
template<typename T, uint8_t bits>
requires std::is_integral_v<T>
constexpr T BITS_MAX()
{
static_assert(bits <= NUM_BITS<T>, "BITS_MAX: Return type cannot represet maximum bit value");
static_assert(bits <= 64, "BITS_MAX: Bits exceed 64");
if constexpr (std::is_unsigned_v<T>)
{
return (1ULL << bits) - 1;
}
return (1LL << (bits - 1)) - 1;
}
static_assert(BITS_MAX<int16_t, 10>() == 511, "BITS_MAX<int16_t>(10) != 511");
static_assert(BITS_MAX<uint16_t, 10>() == 1023, "BITS_MAX<uint16_t>(10) != 1023");
//Minimum value for a given number of bits, result will be signed/unsigned depending on type
template<typename T, uint8_t bits>
requires std::is_integral_v<T>
constexpr T BITS_MIN()
{
static_assert(bits <= NUM_BITS<T>, "BITS_MIN: Return type cannot represet minimum bit value");
static_assert(bits <= 64, "BITS_MIN: Bits exceed 64");
if constexpr (std::is_unsigned_v<T>)
{
return 0;
}
return static_cast<T>(-(1LL << (bits - 1)));
}
static_assert(BITS_MIN<int16_t, 10>() == -512, "BITS_MIN<int16_t>(10) != -512");
static_assert(BITS_MIN<uint16_t, 10>() == 0, "BITS_MIN<uint16_t>(10) != 0");
template <typename T>
static inline T invert(T value)
{
if constexpr (std::is_unsigned_v<T>)
{
return Range::MAX<T> - value;
}
return (value == Range::MIN<T>) ? Range::MAX<T> : -value;
}
template <typename To, typename From>
requires std::is_integral_v<To> && std::is_integral_v<From>
static inline To clamp(From value)
{
return static_cast<To>((value < Range::MIN<To>)
? Range::MIN<To>
: (value > Range::MAX<To>)
? Range::MAX<To>
: value);
}
template <typename T>
static inline T clamp(T value, T min, T max)
{
return (value < min) ? min : (value > max) ? max : value;
}
template <typename To, typename From>
static inline To clamp(From value, To min_to, To max_to)
{
return (value < min_to) ? min_to : (value > max_to) ? max_to : static_cast<To>(value);
}
template <typename To, typename From>
requires std::is_integral_v<To> && std::is_integral_v<From>
static constexpr To scale(From value, From min_from, From max_from, To min_to, To max_to)
{
return static_cast<To>(
(static_cast<int64_t>(value - min_from) * (max_to - min_to) / (max_from - min_from)) + min_to);
}
template <typename To, typename From>
static inline To scale(From value)
{
return scale<To>(value, Range::MIN<From>, Range::MAX<From>, Range::MIN<To>, Range::MAX<To>);
}
template <typename To, typename From>
static inline To scale(From value, To min_to, To max_to)
{
return scale<To>(value, Range::MIN<From>, Range::MAX<From>, min_to, max_to);
}
//Cast value to signed/unsigned for accurate scaling
template <typename To, uint8_t bits, typename From>
static inline To scale_from_bits(From value)
{
return scale<To>(
Range::clamp(value, BITS_MIN<From, bits>(), BITS_MAX<From, bits>()),
BITS_MIN<From, bits>(),
BITS_MAX<From, bits>(),
Range::MIN<To>,
Range::MAX<To>);
}
//Cast value to signed/unsigned for accurate scaling
template <typename To, uint8_t bits, typename From>
static inline To scale_to_bits(From value)
{
return scale<To>(
Range::clamp(value, BITS_MIN<From, bits>(), BITS_MAX<From, bits>()),
Range::MIN<From>,
Range::MAX<From>,
BITS_MIN<To, bits>(),
BITS_MAX<To, bits>());
}
} // namespace Range
namespace Scale //Scale and invert values
{
static inline uint8_t int16_to_uint8(int16_t value)
{
uint16_t shifted_value = static_cast<uint16_t>(value + Range::MID<uint16_t>);
return static_cast<uint8_t>(shifted_value >> 8);
}
static inline uint16_t int16_to_uint16(int16_t value)
{
return static_cast<uint16_t>(value + Range::MID<uint16_t>);
}
static inline int8_t int16_to_int8(int16_t value)
{
return static_cast<int8_t>((value + Range::MID<uint16_t>) >> 8);
}
static inline uint8_t uint16_to_uint8(uint16_t value)
{
return static_cast<uint8_t>(value >> 8);
}
static inline int16_t uint16_to_int16(uint16_t value)
{
return static_cast<int16_t>(value - Range::MID<uint16_t>);
}
static inline int8_t uint16_to_int8(uint16_t value)
{
return static_cast<int8_t>((value >> 8) - Range::MID<uint8_t>);
}
static inline int16_t uint8_to_int16(uint8_t value)
{
return static_cast<int16_t>((static_cast<int32_t>(value) << 8) - Range::MID<uint16_t>);
}
static inline uint16_t uint8_to_uint16(uint8_t value)
{
return static_cast<uint16_t>(value) << 8;
}
static inline int8_t uint8_to_int8(uint8_t value)
{
return static_cast<int8_t>(value - Range::MID<uint8_t>);
}
static inline int16_t int8_to_int16(int8_t value)
{
return static_cast<int16_t>(value) << 8;
}
static inline uint16_t int8_to_uint16(int8_t value)
{
return static_cast<uint16_t>((value + Range::MID<uint8_t>) << 8);
}
static inline uint8_t int8_to_uint8(int8_t value)
{
return static_cast<uint8_t>(value + Range::MID<uint8_t>);
}
} // namespace Scale
#endif // _RANGE_H_

View File

@@ -0,0 +1,106 @@
#ifndef FIX16_EXT_H
#define FIX16_EXT_H
#include <cstdint>
#include "libfixmath/fix16.hpp"
namespace fix16 {
inline Fix16 abs(Fix16 x)
{
return Fix16(fix16_abs(x.value));
}
inline Fix16 rad2deg(Fix16 rad)
{
return Fix16(fix16_rad_to_deg(rad.value));
}
inline Fix16 deg2rad(Fix16 deg)
{
return Fix16(fix16_deg_to_rad(deg.value));
}
inline Fix16 atan(Fix16 x)
{
return Fix16(fix16_atan(x.value));
}
inline Fix16 atan2(Fix16 y, Fix16 x)
{
return Fix16(fix16_atan2(y.value, x.value));
}
inline Fix16 tan(Fix16 x)
{
return Fix16(fix16_tan(x.value));
}
inline Fix16 cos(Fix16 x)
{
return Fix16(fix16_cos(x.value));
}
inline Fix16 sin(Fix16 x)
{
return Fix16(fix16_sin(x.value));
}
inline Fix16 sqrt(Fix16 x)
{
return Fix16(fix16_sqrt(x.value));
}
inline Fix16 sq(Fix16 x)
{
return Fix16(fix16_sq(x.value));
}
inline Fix16 clamp(Fix16 x, Fix16 min, Fix16 max)
{
return Fix16(fix16_clamp(x.value, min.value, max.value));
}
inline Fix16 pow(Fix16 x, Fix16 y)
{
fix16_t& base = x.value;
fix16_t& exponent = y.value;
if (exponent == F16(0.0))
return Fix16(fix16_from_int(1));
if (base == F16(0.0))
return Fix16(fix16_from_int(0));
int32_t int_exp = fix16_to_int(exponent);
if (fix16_from_int(int_exp) == exponent)
{
fix16_t result = F16(1.0);
fix16_t current_base = base;
if (int_exp < 0)
{
current_base = fix16_div(F16(1.0), base);
int_exp = -int_exp;
}
while (int_exp)
{
if (int_exp & 1)
{
result = fix16_mul(result, current_base);
}
current_base = fix16_mul(current_base, current_base);
int_exp >>= 1;
}
return Fix16(result);
}
return Fix16(fix16_exp(fix16_mul(exponent, fix16_log(base))));
}
} // namespace fix16
#endif // FIX16_EXT_H

View File

@@ -0,0 +1,72 @@
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/timers.h>
#include <driver/gpio.h>
#include <esp_log.h>
#include "I2CDriver/I2CDriver.h"
I2CDriver::~I2CDriver()
{
i2c_driver_delete(i2c_port_);
}
void I2CDriver::initialize_i2c(i2c_port_t i2c_port, gpio_num_t sda, gpio_num_t scl, uint32_t clk_speed)
{
if (initialized_)
{
return;
}
i2c_port_ = i2c_port;
i2c_config_t conf;
std::memset(&conf, 0, sizeof(i2c_config_t));
conf.mode = I2C_MODE_MASTER;
conf.sda_io_num = sda;
conf.scl_io_num = scl;
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
conf.master.clk_speed = clk_speed;
i2c_param_config(i2c_port_, &conf);
i2c_driver_install(i2c_port_, conf.mode, 0, 0, 0);
initialized_ = true;
}
void I2CDriver::run_tasks()
{
std::function<void()> task;
while (true)
{
while (task_queue_.pop(task))
{
task();
}
vTaskDelay(1);
}
}
void I2CDriver::write_packet(uint8_t address, const PacketIn& data_in)
{
task_queue_.push([this, address, data_in]()
{
i2c_write_blocking(address, reinterpret_cast<const uint8_t*>(&data_in), sizeof(PacketIn));
});
}
void I2CDriver::read_packet(uint8_t address, std::function<void(const PacketOut&)> callback)
{
task_queue_.push([this, address, callback]()
{
PacketOut data_out;
if (i2c_read_blocking(address, reinterpret_cast<uint8_t*>(&data_out), sizeof(PacketOut)) == ESP_OK)
{
callback(data_out);
}
});
}

View File

@@ -0,0 +1,108 @@
#ifndef _I2C_DRIVER_H_
#define _I2C_DRIVER_H_
#include <cstdint>
#include <cstring>
#include <functional>
#include <driver/i2c.h>
#include "sdkconfig.h"
#include "RingBuffer.h"
#include "UserSettings/DeviceDriverTypes.h"
class I2CDriver
{
public:
static constexpr bool MULTI_SLAVE =
#if CONFIG_MULTI_SLAVE_MODE == 0
false;
#else
true;
#endif
enum class PacketID : uint8_t { UNKNOWN = 0, SET_PAD, GET_PAD, SET_DRIVER };
enum class PacketResp : uint8_t { OK = 1, ERROR };
#pragma pack(push, 1)
struct PacketIn
{
uint8_t packet_len{sizeof(PacketIn)};
PacketID packet_id{static_cast<uint8_t>(PacketID::SET_PAD)};
uint8_t index{0};
DeviceDriverType device_driver{DeviceDriverType::NONE};
uint8_t dpad{0};
uint16_t buttons{0};
uint8_t trigger_l{0};
uint8_t trigger_r{0};
int16_t joystick_lx{0};
int16_t joystick_ly{0};
int16_t joystick_rx{0};
int16_t joystick_ry{0};
std::array<uint8_t, 15> reserved1{0};
};
static_assert(sizeof(PacketIn) == 32, "PacketIn is misaligned");
struct PacketOut
{
uint8_t packet_len{0};
PacketID packet_id{0};
uint8_t index{0};
uint8_t rumble_l{0};
uint8_t rumble_r{0};
std::array<uint8_t, 3> reserved{0};
};
static_assert(sizeof(PacketOut) == 8, "PacketOut is misaligned");
#pragma pack(pop)
I2CDriver() = default;
~I2CDriver();
void initialize_i2c(i2c_port_t i2c_port, gpio_num_t sda, gpio_num_t scl, uint32_t clk_speed);
//Does not return
void run_tasks();
void write_packet(uint8_t address, const PacketIn& data_in);
void read_packet(uint8_t address, std::function<void(const PacketOut&)> callback);
private:
using TaskQueue = RingBuffer<std::function<void()>, CONFIG_I2C_RING_BUFFER_SIZE>;
TaskQueue task_queue_;
i2c_port_t i2c_port_ = I2C_NUM_0;
bool initialized_ = false;
static inline esp_err_t i2c_write_blocking(uint8_t address, const uint8_t* buffer, size_t len)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, true);
i2c_master_write(cmd, buffer, len, true);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, pdMS_TO_TICKS(2));
i2c_cmd_link_delete(cmd);
return ret;
}
static inline esp_err_t i2c_read_blocking(uint8_t address, uint8_t* buffer, size_t len)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_READ, true);
if (len > 1)
{
i2c_master_read(cmd, buffer, len - 1, I2C_MASTER_ACK);
}
i2c_master_read_byte(cmd, buffer + len - 1, I2C_MASTER_NACK);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, pdMS_TO_TICKS(2));
i2c_cmd_link_delete(cmd);
return ret;
}
}; // class I2CDriver
#endif // _I2C_DRIVER_H_

View File

@@ -0,0 +1,70 @@
menu "OGXMini Options"
config I2C_RING_BUFFER_SIZE
int "Set I2C ring buffer size"
default 6
config I2C_PORT
int "Set I2C port"
default 0
config I2C_SDA_PIN
int "Set I2C SDA pin"
default 21
config I2C_SCL_PIN
int "Set I2C SCL pin"
default 22
config I2C_BAUDRATE
int "Set I2C baudrate"
default 1000000
config RESET_PIN
int "Set reset pin"
default 9
config MULTI_SLAVE_MODE
bool "Enable multiple slave devices"
default n
config ENABLE_LED_1
bool "Enable LED 1"
default y
config LED_PIN_1
int "Set LED 1 pin"
default 15
depends on ENABLE_LED_1
config ENABLE_LED_2
bool "Enable LED 2"
default n
depends on ENABLE_LED_1
config LED_PIN_2
int "Set LED 2 pin"
default 255
depends on ENABLE_LED_2
config ENABLE_LED_3
bool "Enable LED 3"
default n
depends on ENABLE_LED_2
config LED_PIN_3
int "Set LED 3 pin"
default 255
depends on ENABLE_LED_3
config ENABLE_LED_4
bool "Enable LED 4"
default n
depends on ENABLE_LED_3
config LED_PIN_4
int "Set LED 4 pin"
default 255
depends on ENABLE_LED_4
endmenu

View File

@@ -0,0 +1,55 @@
#ifndef _RING_BUFFER_H_
#define _RING_BUFFER_H_
#include <cstdint>
#include <atomic>
#include <array>
template<typename Type, size_t SIZE>
class RingBuffer
{
public:
RingBuffer() : head_(0), tail_(0) {}
RingBuffer(const RingBuffer&) = delete;
RingBuffer& operator=(const RingBuffer&) = delete;
RingBuffer(RingBuffer&&) = default;
RingBuffer& operator=(RingBuffer&&) = default;
bool push(const Type& item)
{
size_t head = head_.load(std::memory_order_relaxed);
size_t next_head = (head + 1) % SIZE;
if (next_head == tail_.load(std::memory_order_acquire))
{
tail_.store((tail_.load(std::memory_order_relaxed) + 1) % SIZE, std::memory_order_release);
}
buffer_[head] = item;
head_.store(next_head, std::memory_order_release);
return true;
}
bool pop(Type& item)
{
size_t tail = tail_.load(std::memory_order_relaxed);
if (tail == head_.load(std::memory_order_acquire))
{
return false;
}
item = buffer_[tail];
tail_.store((tail + 1) % SIZE, std::memory_order_release);
return true;
}
private:
std::array<Type, SIZE> buffer_;
std::atomic<size_t> head_;
std::atomic<size_t> tail_;
};
#endif // _RING_BUFFER_H_

View File

@@ -0,0 +1,39 @@
#ifndef _DEVICE_DRIVER_TYPES_H_
#define _DEVICE_DRIVER_TYPES_H_
#include <cstdint>
enum class DeviceDriverType : uint8_t
{
NONE = 0,
XBOXOG,
XBOXOG_SB,
XBOXOG_XR,
XINPUT,
PS3,
DINPUT,
PSCLASSIC,
SWITCH,
WEBAPP = 100,
UART_BRIDGE
};
static inline std::string DRIVER_NAME(DeviceDriverType driver)
{
switch (driver)
{
case DeviceDriverType::XBOXOG: return "XBOX OG";
case DeviceDriverType::XBOXOG_SB: return "XBOX OG SB";
case DeviceDriverType::XBOXOG_XR: return "XBOX OG XR";
case DeviceDriverType::XINPUT: return "XINPUT";
case DeviceDriverType::PS3: return "PS3";
case DeviceDriverType::DINPUT: return "DINPUT";
case DeviceDriverType::PSCLASSIC: return "PS CLASSIC";
case DeviceDriverType::SWITCH: return "SWITCH";
case DeviceDriverType::WEBAPP: return "WEBAPP";
case DeviceDriverType::UART_BRIDGE: return "UART BRIDGE";
default: return "UNKNOWN";
}
}
#endif // _DEVICE_DRIVER_TYPES_H_

View File

@@ -0,0 +1,63 @@
#include "Board/ogxm_log.h"
#include "UserSettings/JoystickSettings.h"
bool JoystickSettings::is_same(const JoystickSettingsRaw& raw) const
{
return dz_inner == Fix16(raw.dz_inner) &&
dz_outer == Fix16(raw.dz_outer) &&
anti_dz_circle == Fix16(raw.anti_dz_circle) &&
anti_dz_circle_y_scale == Fix16(raw.anti_dz_circle_y_scale) &&
anti_dz_square == Fix16(raw.anti_dz_square) &&
anti_dz_square_y_scale == Fix16(raw.anti_dz_square_y_scale) &&
anti_dz_angular == Fix16(raw.anti_dz_angular) &&
anti_dz_outer == Fix16(raw.anti_dz_outer) &&
axis_restrict == Fix16(raw.axis_restrict) &&
angle_restrict == Fix16(raw.angle_restrict) &&
diag_scale_min == Fix16(raw.diag_scale_min) &&
diag_scale_max == Fix16(raw.diag_scale_max) &&
curve == Fix16(raw.curve) &&
uncap_radius == raw.uncap_radius &&
invert_y == raw.invert_y &&
invert_x == raw.invert_x;
}
void JoystickSettings::set_from_raw(const JoystickSettingsRaw& raw)
{
dz_inner = Fix16(raw.dz_inner);
dz_outer = Fix16(raw.dz_outer);
anti_dz_circle = Fix16(raw.anti_dz_circle);
anti_dz_circle_y_scale = Fix16(raw.anti_dz_circle_y_scale);
anti_dz_square = Fix16(raw.anti_dz_square);
anti_dz_square_y_scale = Fix16(raw.anti_dz_square_y_scale);
anti_dz_angular = Fix16(raw.anti_dz_angular);
anti_dz_outer = Fix16(raw.anti_dz_outer);
axis_restrict = Fix16(raw.axis_restrict);
angle_restrict = Fix16(raw.angle_restrict);
diag_scale_min = Fix16(raw.diag_scale_min);
diag_scale_max = Fix16(raw.diag_scale_max);
curve = Fix16(raw.curve);
uncap_radius = raw.uncap_radius;
invert_y = raw.invert_y;
invert_x = raw.invert_x;
}
void JoystickSettingsRaw::log_values()
{
OGXM_LOG("dz_inner: %f\n", fix16_to_float(dz_inner));
OGXM_LOG_HEX("dz_inner: ", reinterpret_cast<uint8_t*>(&dz_inner), sizeof(dz_inner));
OGXM_LOG("dz_outer: %f\n", fix16_to_float(dz_outer));
OGXM_LOG("anti_dz_circle: %f\n", fix16_to_float(anti_dz_circle));
OGXM_LOG("anti_dz_circle_y_scale: %f\n", fix16_to_float(anti_dz_circle_y_scale));
OGXM_LOG("anti_dz_square: %f\n", fix16_to_float(anti_dz_square));
OGXM_LOG("anti_dz_square_y_scale: %f\n", fix16_to_float(anti_dz_square_y_scale));
OGXM_LOG("anti_dz_angular: %f\n", fix16_to_float(anti_dz_angular));
OGXM_LOG("anti_dz_outer: %f\n", fix16_to_float(anti_dz_outer));
OGXM_LOG("axis_restrict: %f\n", fix16_to_float(axis_restrict));
OGXM_LOG("angle_restrict: %f\n", fix16_to_float(angle_restrict));
OGXM_LOG("diag_scale_min: %f\n", fix16_to_float(diag_scale_min));
OGXM_LOG("diag_scale_max: %f\n", fix16_to_float(diag_scale_max));
OGXM_LOG("curve: %f\n", fix16_to_float(curve));
OGXM_LOG("uncap_radius: %d\n", uncap_radius);
OGXM_LOG("invert_y: %d\n", invert_y);
OGXM_LOG("invert_x: %d\n", invert_x);
}

View File

@@ -0,0 +1,68 @@
#ifndef _JOYSTICK_SETTINGS_H_
#define _JOYSTICK_SETTINGS_H_
#include <cstdint>
#include "libfixmath/fix16.hpp"
struct JoystickSettingsRaw;
struct JoystickSettings
{
Fix16 dz_inner{Fix16(0.0f)};
Fix16 dz_outer{Fix16(1.0f)};
Fix16 anti_dz_circle{Fix16(0.0f)};
Fix16 anti_dz_circle_y_scale{Fix16(0.0f)};
Fix16 anti_dz_square{Fix16(0.0f)};
Fix16 anti_dz_square_y_scale{Fix16(0.0f)};
Fix16 anti_dz_angular{Fix16(0.0f)};
Fix16 anti_dz_outer{Fix16(1.0f)};
Fix16 axis_restrict{Fix16(0.0f)};
Fix16 angle_restrict{Fix16(0.0f)};
Fix16 diag_scale_min{Fix16(1.0f)};
Fix16 diag_scale_max{Fix16(1.0f)};
Fix16 curve{Fix16(1.0f)};
bool uncap_radius{true};
bool invert_y{false};
bool invert_x{false};
bool is_same(const JoystickSettingsRaw& raw) const;
void set_from_raw(const JoystickSettingsRaw& raw);
};
#pragma pack(push, 1)
struct JoystickSettingsRaw
{
fix16_t dz_inner{fix16_from_int(0)};
fix16_t dz_outer{fix16_from_int(1)};
fix16_t anti_dz_circle{fix16_from_int(0)};
fix16_t anti_dz_circle_y_scale{fix16_from_int(0)};
fix16_t anti_dz_square{fix16_from_int(0)};
fix16_t anti_dz_square_y_scale{fix16_from_int(0)};
fix16_t anti_dz_angular{fix16_from_int(0)};
fix16_t anti_dz_outer{fix16_from_int(1)};
fix16_t axis_restrict{fix16_from_int(0)};
fix16_t angle_restrict{fix16_from_int(0)};
fix16_t diag_scale_min{fix16_from_int(1)};
fix16_t diag_scale_max{fix16_from_int(1)};
fix16_t curve{fix16_from_int(1)};
uint8_t uncap_radius{true};
uint8_t invert_y{false};
uint8_t invert_x{false};
void log_values();
};
static_assert(sizeof(JoystickSettingsRaw) == 55, "JoystickSettingsRaw is an unexpected size");
#pragma pack(pop)
#endif // _JOYSTICK_SETTINGS_H_

View File

@@ -0,0 +1,113 @@
#ifndef _NVS_HELPER_H_
#define _NVS_HELPER_H_
#include <cstdint>
#include <array>
#include <string>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <nvs_flash.h>
#include <nvs.h>
#include <esp_err.h>
class NVSHelper
{
public:
static NVSHelper& get_instance()
{
static NVSHelper instance;
return instance;
}
esp_err_t write(const std::string& key, const void* value, size_t len)
{
esp_err_t err;
nvs_handle_t handle;
xSemaphoreTake(nvs_mutex_, portMAX_DELAY);
if ((err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle)) != ESP_OK)
{
xSemaphoreGive(nvs_mutex_);
return err;
}
if ((err = nvs_set_blob(handle, key.c_str(), value, len)) != ESP_OK)
{
nvs_close(handle);
xSemaphoreGive(nvs_mutex_);
return err;
}
err = nvs_commit(handle);
nvs_close(handle);
xSemaphoreGive(nvs_mutex_);
return err;
}
esp_err_t read(const std::string& key, void* value, size_t len)
{
esp_err_t err;
nvs_handle_t handle;
xSemaphoreTake(nvs_mutex_, portMAX_DELAY);
if ((err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle)) != ESP_OK)
{
xSemaphoreGive(nvs_mutex_);
return err;
}
err = nvs_get_blob(handle, key.c_str(), value, &len);
nvs_close(handle);
xSemaphoreGive(nvs_mutex_);
return err;
}
esp_err_t erase_all()
{
esp_err_t err;
nvs_handle_t handle;
xSemaphoreTake(nvs_mutex_, portMAX_DELAY);
if ((err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle)) != ESP_OK)
{
xSemaphoreGive(nvs_mutex_);
return err;
}
err = nvs_erase_all(handle);
nvs_close(handle);
xSemaphoreGive(nvs_mutex_);
return err;
}
private:
NVSHelper()
{
nvs_mutex_ = xSemaphoreCreateMutex();
xSemaphoreTake(nvs_mutex_, portMAX_DELAY);
if (nvs_flash_init() != ESP_OK)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ESP_ERROR_CHECK(nvs_flash_init());
}
xSemaphoreGive(nvs_mutex_);
}
~NVSHelper() = default;
NVSHelper(const NVSHelper&) = delete;
NVSHelper& operator=(const NVSHelper&) = delete;
SemaphoreHandle_t nvs_mutex_;
static constexpr char NVS_NAMESPACE[] = "user_data";
}; // class NVSHelper
#endif // _NVS_HELPER_H_

View File

@@ -0,0 +1,29 @@
#include "Board/ogxm_log.h"
#include "UserSettings/TriggerSettings.h"
bool TriggerSettings::is_same(const TriggerSettingsRaw& raw) const
{
return dz_inner == Fix16(raw.dz_inner) &&
dz_outer == Fix16(raw.dz_outer) &&
anti_dz_inner == Fix16(raw.anti_dz_inner) &&
anti_dz_outer == Fix16(raw.anti_dz_outer) &&
curve == Fix16(raw.curve);
}
void TriggerSettings::set_from_raw(const TriggerSettingsRaw& raw)
{
dz_inner = Fix16(raw.dz_inner);
dz_outer = Fix16(raw.dz_outer);
anti_dz_inner = Fix16(raw.anti_dz_inner);
anti_dz_outer = Fix16(raw.anti_dz_outer);
curve = Fix16(raw.curve);
}
void TriggerSettingsRaw::log_values()
{
OGXM_LOG("dz_inner: %f\n", fix16_to_float(dz_inner));
OGXM_LOG("dz_outer: %f\n", fix16_to_float(dz_outer));
OGXM_LOG("anti_dz_inner: %f\n", fix16_to_float(anti_dz_inner));
OGXM_LOG("anti_dz_outer: %f\n", fix16_to_float(anti_dz_outer));
OGXM_LOG("curve: %f\n", fix16_to_float(curve));
}

View File

@@ -0,0 +1,40 @@
#ifndef TRIGGER_SETTINGS_H
#define TRIGGER_SETTINGS_H
#include <cstdint>
#include "libfixmath/fix16.hpp"
struct TriggerSettingsRaw;
struct TriggerSettings
{
Fix16 dz_inner{Fix16(0.0f)};
Fix16 dz_outer{Fix16(1.0f)};
Fix16 anti_dz_inner{Fix16(0.0f)};
Fix16 anti_dz_outer{Fix16(1.0f)};
Fix16 curve{Fix16(1.0f)};
bool is_same(const TriggerSettingsRaw& raw) const;
void set_from_raw(const TriggerSettingsRaw& raw);
};
#pragma pack(push, 1)
struct TriggerSettingsRaw
{
fix16_t dz_inner{fix16_from_int(0)};
fix16_t dz_outer{fix16_from_int(1)};
fix16_t anti_dz_inner{fix16_from_int(0)};
fix16_t anti_dz_outer{fix16_from_int(1)};
fix16_t curve{fix16_from_int(1)};
void log_values();
};
static_assert(sizeof(TriggerSettingsRaw) == 20, "TriggerSettingsRaw is an unexpected size");
#pragma pack(pop)
#endif // TRIGGER_SETTINGS_H

View File

@@ -0,0 +1,40 @@
#include <cstring>
#include "Gamepad/Gamepad.h"
#include "UserSettings/UserProfile.h"
UserProfile::UserProfile()
{
id = 1;
dpad_up = Gamepad::DPAD_UP;
dpad_down = Gamepad::DPAD_DOWN;
dpad_left = Gamepad::DPAD_LEFT;
dpad_right = Gamepad::DPAD_RIGHT;
button_a = Gamepad::BUTTON_A;
button_b = Gamepad::BUTTON_B;
button_x = Gamepad::BUTTON_X;
button_y = Gamepad::BUTTON_Y;
button_l3 = Gamepad::BUTTON_L3;
button_r3 = Gamepad::BUTTON_R3;
button_back = Gamepad::BUTTON_BACK;
button_start = Gamepad::BUTTON_START;
button_lb = Gamepad::BUTTON_LB;
button_rb = Gamepad::BUTTON_RB;
button_sys = Gamepad::BUTTON_SYS;
button_misc = Gamepad::BUTTON_MISC;
analog_enabled = 0;
analog_off_up = Gamepad::ANALOG_OFF_UP;
analog_off_down = Gamepad::ANALOG_OFF_DOWN;
analog_off_left = Gamepad::ANALOG_OFF_LEFT;
analog_off_right = Gamepad::ANALOG_OFF_RIGHT;
analog_off_a = Gamepad::ANALOG_OFF_A;
analog_off_b = Gamepad::ANALOG_OFF_B;
analog_off_x = Gamepad::ANALOG_OFF_X;
analog_off_y = Gamepad::ANALOG_OFF_Y;
analog_off_lb = Gamepad::ANALOG_OFF_LB;
analog_off_rb = Gamepad::ANALOG_OFF_RB;
}

View File

@@ -0,0 +1,55 @@
#ifndef _USER_PROFILE_H_
#define _USER_PROFILE_H_
#include <cstdint>
#include "UserSettings/JoystickSettings.h"
#include "UserSettings/TriggerSettings.h"
#pragma pack(push, 1)
struct UserProfile
{
uint8_t id;
JoystickSettingsRaw joystick_settings_l;
JoystickSettingsRaw joystick_settings_r;
TriggerSettingsRaw trigger_settings_l;
TriggerSettingsRaw trigger_settings_r;
uint8_t dpad_up;
uint8_t dpad_down;
uint8_t dpad_left;
uint8_t dpad_right;
uint16_t button_a;
uint16_t button_b;
uint16_t button_x;
uint16_t button_y;
uint16_t button_l3;
uint16_t button_r3;
uint16_t button_back;
uint16_t button_start;
uint16_t button_lb;
uint16_t button_rb;
uint16_t button_sys;
uint16_t button_misc;
uint8_t analog_enabled;
uint8_t analog_off_up;
uint8_t analog_off_down;
uint8_t analog_off_left;
uint8_t analog_off_right;
uint8_t analog_off_a;
uint8_t analog_off_b;
uint8_t analog_off_x;
uint8_t analog_off_y;
uint8_t analog_off_lb;
uint8_t analog_off_rb;
UserProfile();
};
static_assert(sizeof(UserProfile) == 190, "UserProfile struct size mismatch");
#pragma pack(pop)
#endif // _USER_PROFILE_H_

View File

@@ -0,0 +1,273 @@
#include <cstring>
#include <array>
#include <string>
#include <nvs_flash.h>
#include <nvs.h>
#include <esp_err.h>
#include <esp_log.h>
#include "Gamepad/Gamepad.h"
#include "UserSettings/NVSHelper.h"
static constexpr uint32_t BUTTON_COMBO(const uint16_t& buttons, const uint8_t& dpad = 0)
{
return (static_cast<uint32_t>(buttons) << 16) | static_cast<uint32_t>(dpad);
}
namespace ButtonCombo
{
static constexpr uint32_t PS3 = BUTTON_COMBO(Gamepad::BUTTON_START, Gamepad::DPAD_LEFT);
static constexpr uint32_t DINPUT = BUTTON_COMBO(Gamepad::BUTTON_START | Gamepad::BUTTON_RB, Gamepad::DPAD_LEFT);
static constexpr uint32_t XINPUT = BUTTON_COMBO(Gamepad::BUTTON_START, Gamepad::DPAD_UP);
static constexpr uint32_t SWITCH = BUTTON_COMBO(Gamepad::BUTTON_START, Gamepad::DPAD_DOWN);
static constexpr uint32_t XBOXOG = BUTTON_COMBO(Gamepad::BUTTON_START, Gamepad::DPAD_RIGHT);
static constexpr uint32_t XBOXOG_SB = BUTTON_COMBO(Gamepad::BUTTON_START | Gamepad::BUTTON_RB, Gamepad::DPAD_RIGHT);
static constexpr uint32_t XBOXOG_XR = BUTTON_COMBO(Gamepad::BUTTON_START | Gamepad::BUTTON_LB, Gamepad::DPAD_RIGHT);
static constexpr uint32_t PSCLASSIC = BUTTON_COMBO(Gamepad::BUTTON_START | Gamepad::BUTTON_A);
static constexpr uint32_t WEBAPP = BUTTON_COMBO(Gamepad::BUTTON_START | Gamepad::BUTTON_LB | Gamepad::BUTTON_RB);
};
static constexpr DeviceDriverType VALID_DRIVER_TYPES[] =
{
#if MAX_GAMEPADS > 1
DeviceDriverType::DINPUT,
DeviceDriverType::SWITCH,
#else // MAX_GAMEPADS == 1
DeviceDriverType::XBOXOG,
// DeviceDriverType::XBOXOG_SB,
DeviceDriverType::XBOXOG_XR,
DeviceDriverType::DINPUT,
DeviceDriverType::SWITCH,
DeviceDriverType::PS3,
DeviceDriverType::PSCLASSIC,
DeviceDriverType::XINPUT,
#endif
};
struct ComboMap { uint32_t combo; DeviceDriverType driver; };
static constexpr std::array<ComboMap, 9> BUTTON_COMBO_MAP =
{{
{ ButtonCombo::XBOXOG, DeviceDriverType::XBOXOG },
{ ButtonCombo::XBOXOG_SB, DeviceDriverType::XBOXOG_SB },
{ ButtonCombo::XBOXOG_XR, DeviceDriverType::XBOXOG_XR },
{ ButtonCombo::WEBAPP, DeviceDriverType::WEBAPP },
{ ButtonCombo::DINPUT, DeviceDriverType::DINPUT },
{ ButtonCombo::SWITCH, DeviceDriverType::SWITCH },
{ ButtonCombo::XINPUT, DeviceDriverType::XINPUT },
{ ButtonCombo::PS3, DeviceDriverType::PS3 },
{ ButtonCombo::PSCLASSIC, DeviceDriverType::PSCLASSIC }
}};
const std::string UserSettings::INIT_FLAG_KEY()
{
return std::string("init_flag");
}
const std::string UserSettings::PROFILE_KEY(const uint8_t profile_id)
{
return std::string("profile_") + std::to_string(profile_id);
}
const std::string UserSettings::ACTIVE_PROFILE_KEY(const uint8_t index)
{
return std::string("active_id_") + std::to_string(index);
}
const std::string UserSettings::DRIVER_TYPE_KEY()
{
return std::string("driver_type");
}
const std::string UserSettings::FIRMWARE_VER_KEY()
{
return std::string("firmware_ver");
}
void UserSettings::initialize_flash()
{
ESP_LOGD("UserSettings", "Checking for UserSettings init flag");
uint8_t init_flag = 0;
ESP_ERROR_CHECK(nvs_helper_.read(INIT_FLAG_KEY(), &init_flag, sizeof(init_flag)));
if (init_flag == INIT_FLAG)
{
ESP_LOGD("UserSettings", "UserSettings already initialized");
return;
}
ESP_ERROR_CHECK(nvs_helper_.erase_all());
OGXM_LOG("Initializing UserSettings\n");
current_driver_ = DEFAULT_DRIVER();
uint8_t driver_type = static_cast<uint8_t>(current_driver_);
ESP_ERROR_CHECK(nvs_helper_.write(DRIVER_TYPE_KEY(), &driver_type, sizeof(driver_type)));
uint8_t active_id = 1;
for (uint8_t i = 0; i < MAX_GAMEPADS; i++)
{
ESP_ERROR_CHECK(nvs_helper_.write(ACTIVE_PROFILE_KEY(i), &active_id, sizeof(active_id)));
}
UserProfile profile = UserProfile();
for (uint8_t i = 0; i < MAX_PROFILES; i++)
{
profile.id = i + 1;
ESP_ERROR_CHECK(nvs_helper_.write(PROFILE_KEY(profile.id), &profile, sizeof(UserProfile)));
}
init_flag = INIT_FLAG;
ESP_ERROR_CHECK(nvs_helper_.write(INIT_FLAG_KEY(), &init_flag, sizeof(init_flag)));
}
DeviceDriverType UserSettings::get_current_driver()
{
if (current_driver_ != DeviceDriverType::NONE)
{
return current_driver_;
}
uint8_t stored_type = 0;
nvs_helper_.read(DRIVER_TYPE_KEY(), &stored_type, sizeof(stored_type));
if (is_valid_driver(static_cast<DeviceDriverType>(stored_type)))
{
current_driver_ = static_cast<DeviceDriverType>(stored_type);
return current_driver_;
}
current_driver_ = DEFAULT_DRIVER();
return current_driver_;
}
//Checks if button combo has been held for 3 seconds, returns true if mode has been changed
bool UserSettings::check_for_driver_change(const I2CDriver::PacketIn& packet_in)
{
static uint32_t last_button_combo = BUTTON_COMBO(packet_in.buttons, packet_in.dpad);
static uint8_t call_count = 0;
uint32_t current_button_combo = BUTTON_COMBO(packet_in.buttons, packet_in.dpad);
if (!(current_button_combo & (static_cast<uint32_t>(Gamepad::BUTTON_START) << 16)) ||
last_button_combo != current_button_combo)
{
last_button_combo = current_button_combo;
call_count = 0;
return false;
}
++call_count;
if (call_count < GP_CHECK_COUNT)
{
return false;
}
call_count = 0;
DeviceDriverType new_driver = DeviceDriverType::NONE;
for (const auto& combo : BUTTON_COMBO_MAP)
{
if (combo.combo == current_button_combo &&
is_valid_driver(combo.driver))
{
new_driver = combo.driver;
break;
}
}
if (new_driver == DeviceDriverType::NONE || new_driver == current_driver_)
{
return false;
}
current_driver_ = new_driver;
return true;
}
void UserSettings::store_driver_type(DeviceDriverType new_driver_type)
{
if (!is_valid_driver(new_driver_type))
{
return;
}
uint8_t new_driver = static_cast<uint8_t>(new_driver_type);
nvs_helper_.write(DRIVER_TYPE_KEY(), &new_driver, sizeof(new_driver));
}
void UserSettings::store_profile(const uint8_t index, UserProfile& profile)
{
if (index > MAX_GAMEPADS || profile.id < 1 || profile.id > MAX_PROFILES)
{
return;
}
OGXM_LOG("Storing profile %d for gamepad %d\n", profile.id, index);
if (nvs_helper_.write(PROFILE_KEY(profile.id), &profile, sizeof(UserProfile)) == ESP_OK)
{
OGXM_LOG("Profile %d stored successfully\n", profile.id);
nvs_helper_.write(ACTIVE_PROFILE_KEY(index), &profile.id, sizeof(profile.id));
}
}
void UserSettings::store_profile_and_driver_type(DeviceDriverType new_driver_type, const uint8_t index, UserProfile& profile)
{
store_driver_type(new_driver_type);
store_profile(index, profile);
}
uint8_t UserSettings::get_active_profile_id(const uint8_t index)
{
uint8_t read_profile_id = 0;
if (nvs_helper_.read(ACTIVE_PROFILE_KEY(index), &read_profile_id, sizeof(read_profile_id)) != ESP_OK ||
read_profile_id < 1 ||
read_profile_id > MAX_PROFILES)
{
return 0x01;
}
return read_profile_id;
}
UserProfile UserSettings::get_profile_by_index(const uint8_t index)
{
return get_profile_by_id(get_active_profile_id(index));
}
UserProfile UserSettings::get_profile_by_id(const uint8_t profile_id)
{
UserProfile profile;
if (profile_id < 1 ||
profile_id > MAX_PROFILES ||
nvs_helper_.read(PROFILE_KEY(profile_id), &profile, sizeof(UserProfile)) != ESP_OK)
{
return UserProfile();
}
return profile;
}
DeviceDriverType UserSettings::DEFAULT_DRIVER()
{
return VALID_DRIVER_TYPES[0];
}
bool UserSettings::is_valid_driver(DeviceDriverType mode)
{
for (const auto& driver : VALID_DRIVER_TYPES)
{
if (mode == driver)
{
return true;
}
}
return false;
}

View File

@@ -0,0 +1,60 @@
#ifndef _USER_SETTINGS_H_
#define _USER_SETTINGS_H_
#include <cstdint>
#include <atomic>
#include "sdkconfig.h"
#include "I2CDriver/I2CDriver.h"
#include "UserSettings/UserProfile.h"
#include "UserSettings/DeviceDriverTypes.h"
#include "UserSettings/NVSHelper.h"
class UserSettings
{
public:
static constexpr uint8_t MAX_PROFILES = 8;
static constexpr uint32_t GP_CHECK_DELAY_MS = 1000;
static UserSettings& get_instance()
{
static UserSettings instance;
return instance;
}
void initialize_flash();
DeviceDriverType get_current_driver();
bool check_for_driver_change(const I2CDriver::PacketIn& packet_in);
UserProfile get_profile_by_index(const uint8_t index);
UserProfile get_profile_by_id(const uint8_t profile_id);
uint8_t get_active_profile_id(const uint8_t index);
void store_driver_type(DeviceDriverType new_driver_type);
void store_profile(uint8_t index, UserProfile& profile);
void store_profile_and_driver_type(DeviceDriverType new_driver_type, uint8_t index, UserProfile& profile);
private:
UserSettings() = default;
~UserSettings() = default;
UserSettings(const UserSettings&) = delete;
UserSettings& operator=(const UserSettings&) = delete;
static constexpr uint8_t GP_CHECK_COUNT = 3000 / GP_CHECK_DELAY_MS;
static constexpr uint8_t INIT_FLAG = 0x12;
NVSHelper& nvs_helper_{NVSHelper::get_instance()};
DeviceDriverType current_driver_{DeviceDriverType::NONE};
bool is_valid_driver(DeviceDriverType mode);
DeviceDriverType DEFAULT_DRIVER();
const std::string INIT_FLAG_KEY();
const std::string PROFILE_KEY(const uint8_t profile_id);
const std::string ACTIVE_PROFILE_KEY(const uint8_t index);
const std::string DRIVER_TYPE_KEY();
const std::string FIRMWARE_VER_KEY();
};
#endif // _USER_SETTINGS_H_

View File

@@ -0,0 +1,82 @@
#ifndef _ESP_BTSTACK_CONFIG_H
#define _ESP_BTSTACK_CONFIG_H
// BTstack features that can be enabled
// #define ENABLE_LE_PERIPHERAL
// #define ENABLE_LE_CENTRAL
// #define ENABLE_L2CAP_LE_CREDIT_BASED_FLOW_CONTROL_MODE
// #define ENABLE_LOG_INFO
// #define ENABLE_LOG_ERROR
// #define ENABLE_PRINTF_HEXDUMP
#define ENABLE_SCO_OVER_HCI
// BTstack configuration. buffers, sizes, ...
#define HCI_OUTGOING_PRE_BUFFER_SIZE 4
#define HCI_ACL_PAYLOAD_SIZE (1691 + 4)
#define HCI_ACL_CHUNK_SIZE_ALIGNMENT 4
// #define MAX_NR_AVDTP_CONNECTIONS 1
// #define MAX_NR_AVDTP_STREAM_ENDPOINTS 1
// #define MAX_NR_AVRCP_CONNECTIONS 2
// #define MAX_NR_BNEP_CHANNELS 1
// #define MAX_NR_BNEP_SERVICES 1
// #define MAX_NR_BTSTACK_LINK_KEY_DB_MEMORY_ENTRIES 2
// #define MAX_NR_GATT_CLIENTS 4
// #define MAX_NR_HCI_CONNECTIONS 4
// #define MAX_NR_HID_HOST_CONNECTIONS 4
// #define MAX_NR_HIDS_CLIENTS 4
// #define MAX_NR_HFP_CONNECTIONS 1
// #define MAX_NR_L2CAP_CHANNELS 6
// #define MAX_NR_L2CAP_SERVICES 5
// #define MAX_NR_RFCOMM_CHANNELS 1
// #define MAX_NR_RFCOMM_MULTIPLEXERS 1
// #define MAX_NR_RFCOMM_SERVICES 1
// #define MAX_NR_SERVICE_RECORD_ITEMS 4
// #define MAX_NR_SM_LOOKUP_ENTRIES 3
// #define MAX_NR_WHITELIST_ENTRIES 16
// #define MAX_NR_LE_DEVICE_DB_ENTRIES 16
// Limit number of ACL/SCO Buffer to use by stack to avoid cyw43 shared bus overrun
#define MAX_NR_CONTROLLER_ACL_BUFFERS 6
#define MAX_NR_CONTROLLER_SCO_PACKETS 6
// Enable and configure HCI Controller to Host Flow Control to avoid cyw43 shared bus overrun
#define ENABLE_HCI_CONTROLLER_TO_HOST_FLOW_CONTROL
#define HCI_HOST_ACL_PACKET_LEN 1024
#define HCI_HOST_ACL_PACKET_NUM 6
#define HCI_HOST_SCO_PACKET_LEN 120
#define HCI_HOST_SCO_PACKET_NUM 6
#ifndef HCI_INCOMING_PRE_BUFFER_SIZE
#define HCI_INCOMING_PRE_BUFFER_SIZE 6
#endif
// Link Key DB and LE Device DB using TLV on top of Flash Sector interface
// #define NVM_NUM_DEVICE_DB_ENTRIES 16
// #define NVM_NUM_LINK_KEYS 16
// We don't give btstack a malloc, so use a fixed-size ATT DB.
#define MAX_ATT_DB_SIZE 512
// #define HAVE_MALLOC
// BTstack HAL configuration
// #define HAVE_EMBEDDED_TIME_MS
// // map btstack_assert onto Pico SDK assert()
// #define HAVE_ASSERT
// // Some USB dongles take longer to respond to HCI reset (e.g. BCM20702A).
#define HCI_RESET_RESEND_TIMEOUT_MS 1000
// #define ENABLE_SOFTWARE_AES128
// #define ENABLE_MICRO_ECC_FOR_LE_SECURE_CONNECTIONS
// #define HAVE_BTSTACK_STDIN
// To get the audio demos working even with HCI dump at 115200, this truncates long ACL packetws
#define HCI_DUMP_STDOUT_MAX_SIZE_ACL 100
#ifdef ENABLE_CLASSIC
#define ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE
#endif
#endif // _ESP_BTSTACK_CONFIG_H

View File

@@ -0,0 +1,9 @@
#include <stdlib.h>
#include <stdint.h>
#include "main.h"
void app_main(void)
{
cpp_main();
}

View File

@@ -0,0 +1,22 @@
#include <functional>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "main.h"
#include "sdkconfig.h"
#include "BTManager/BTManager.h"
void cpp_main()
{
xTaskCreatePinnedToCore(
[](void* parameter)
{
BTManager::get_instance().run_task();
},
"bp32",
2048 * 4,
NULL,
configMAX_PRIORITIES-4,
NULL,
0 );
}

View File

@@ -0,0 +1,16 @@
#ifndef _C_WRAPPER_H_
#define _C_WRAPPER_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
void cpp_main();
#ifdef __cplusplus
}
#endif
#endif // _C_WRAPPER_H_

2420
Firmware/ESP32/sdkconfig Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,42 @@
#
# ESP32 options
#
CONFIG_COMPILER_OPTIMIZATION_PERF=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_ESP32_XTAL_FREQ_AUTO=y
CONFIG_XTAL_FREQ_AUTO=y
# Bluetooh related
CONFIG_BT_ENABLED=y
CONFIG_BT_CONTROLLER_ONLY=y
# Enable Dualmode (BLE and BR/EDR)
CONFIG_BTDM_CTRL_MODE_BTDM=y
#CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=y
# Should be, at least, one more than CONFIG_BLUEPAD32_MAX_DEVICES
# See: https://gitlab.com/ricardoquesada/bluepad32/-/issues/11
CONFIG_BTDM_CTRL_BR_EDR_MAX_ACL_CONN=5
CONFIG_BTDM_CTRL_BLE_MAX_CONN=5
CONFIG_BTDM_CTRL_BR_EDR_MAX_SYNC_CONN=0
CONFIG_BTDM_CTRL_AUTO_LATENCY=y
CONFIG_BTDM_CTRL_MODEM_SLEEP=n
CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=y
#
# FreeRTOS
#
# Enable FreeRTOS stats formatting functions, needed for 'tasks' console command
CONFIG_FREERTOS_USE_TRACE_FACILITY=y
CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
#
# Bluepad32 options
#
CONFIG_BLUEPAD32_MAX_DEVICES=4
CONFIG_BLUEPAD32_PLATFORM_CUSTOM=y
#
# Add here other options
#

2
Firmware/FWDefines.cmake Normal file
View File

@@ -0,0 +1,2 @@
set(FW_NAME "OGX-Mini")
set(FW_VERSION "v1.0.0a3")

View File

@@ -0,0 +1,20 @@
{
"configurations": [
{
"name": "Pico",
"includePath": [
"${PICO_SDK_PATH}/**"
],
"forcedInclude": [
"${PICO_SDK_PATH}/src/common/pico_base/include/pico.h",
"${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h"
],
"defines": [],
"compileCommands": "${workspaceFolder}/build/compile_commands.json",
"cStandard": "c17",
"cppStandard": "c++14",
"intelliSenseMode": "linux-gcc-arm"
}
],
"version": 4
}

13
Firmware/RP2040/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,13 @@
{
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"cmake.configureArgs": [
"-DOGXM_BOARD=ESP32_BLUERETRO_I2C",
// "-DOGXM_RETAIL=TRUE",
"-DMAX_GAMEPADS=1"
],
"files.associations": {
}
}

View File

@@ -0,0 +1,417 @@
cmake_minimum_required(VERSION 3.13)
include(${CMAKE_CURRENT_LIST_DIR}/../FWDefines.cmake)
set(CMAKE_C_COMPILER arm-none-eabi-gcc)
set(CMAKE_CXX_COMPILER arm-none-eabi-g++)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(SRC ${CMAKE_CURRENT_LIST_DIR}/src)
set(EXTERNAL_DIR ${CMAKE_CURRENT_LIST_DIR}/../external)
set(PICOSDK_VERSION_TAG "2.1.0")
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/init_submodules.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/patch_libs.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/generate_gatt_header.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/cmake/get_pico_sdk.cmake)
set(PICO_PIO_USB_PATH ${EXTERNAL_DIR}/Pico-PIO-USB)
set(PICO_TINYUSB_PATH ${EXTERNAL_DIR}/tinyusb)
set(BLUEPAD32_ROOT ${EXTERNAL_DIR}/bluepad32)
set(BTSTACK_ROOT ${BLUEPAD32_ROOT}/external/btstack)
set(PICO_BTSTACK_PATH ${BTSTACK_ROOT})
set(LIBFIXMATH_PATH ${EXTERNAL_DIR}/libfixmath)
get_pico_sdk(${EXTERNAL_DIR} ${PICOSDK_VERSION_TAG})
init_git_submodules(${EXTERNAL_DIR}
${BLUEPAD32_ROOT}
${PICO_TINYUSB_PATH}
)
apply_lib_patches(${EXTERNAL_DIR})
set(SOURCES_BOARD
${SRC}/main.cpp
${SRC}/OGXMini/OGXMini.cpp
${SRC}/OGXMini/Board/Standard.cpp
${SRC}/OGXMini/Board/PicoW.cpp
${SRC}/OGXMini/Board/Four_Channel_I2C.cpp
${SRC}/OGXMini/Board/ESP32_Bluepad32_I2C.cpp
${SRC}/OGXMini/Board/ESP32_Blueretro_I2C.cpp
${SRC}/TaskQueue/TaskQueue.cpp
${SRC}/Board/ogxm_log.cpp
${SRC}/Board/esp32_api.cpp
${SRC}/Board/board_api.cpp
${SRC}/Board/board_api_private/board_api_led.cpp
${SRC}/Board/board_api_private/board_api_rgb.cpp
${SRC}/Board/board_api_private/board_api_bt.cpp
${SRC}/Board/board_api_private/board_api_usbh.cpp
${SRC}/UserSettings/UserSettings.cpp
${SRC}/UserSettings/UserProfile.cpp
${SRC}/UserSettings/JoystickSettings.cpp
${SRC}/UserSettings/TriggerSettings.cpp
${SRC}/USBDevice/tud_callbacks.cpp
${SRC}/USBDevice/DeviceManager.cpp
${SRC}/USBDevice/DeviceDriver/DeviceDriver.cpp
${SRC}/USBDevice/DeviceDriver/PSClassic/PSClassic.cpp
${SRC}/USBDevice/DeviceDriver/PS3/PS3.cpp
${SRC}/USBDevice/DeviceDriver/Switch/Switch.cpp
${SRC}/USBDevice/DeviceDriver/XInput/XInput.cpp
${SRC}/USBDevice/DeviceDriver/XboxOG/XboxOG_GP.cpp
${SRC}/USBDevice/DeviceDriver/XboxOG/XboxOG_SB.cpp
${SRC}/USBDevice/DeviceDriver/XboxOG/XboxOG_XR.cpp
${SRC}/USBDevice/DeviceDriver/DInput/DInput.cpp
${SRC}/USBDevice/DeviceDriver/WebApp/WebApp.cpp
${SRC}/USBDevice/DeviceDriver/XInput/tud_xinput/tud_xinput.cpp
${SRC}/USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid.cpp
)
set(LIBS_BOARD
# Base
pico_stdlib
pico_multicore
pico_rand
hardware_timer
hardware_clocks
hardware_flash
tinyusb_device
tinyusb_board
# UART
hardware_uart
hardware_irq
#fix16
libfixmath
)
set(INC_DIRS_BOARD
)
# Config options
set(MAX_GAMEPADS 1 CACHE STRING "Set number of gamepads, 1 to 4")
if (MAX_GAMEPADS GREATER 4 OR MAX_GAMEPADS LESS 1)
message(FATAL_ERROR "MAX_GAMEPADS must be between 1 and 4")
endif()
add_definitions(-DMAX_GAMEPADS=${MAX_GAMEPADS})
set(OGXM_BOARD "PI_PICO" CACHE STRING "Set board type, options can be found in src/board_config.h")
set(FLASH_SIZE_MB 2)
set(PICO_BOARD none)
if (OGXM_BOARD STREQUAL "PI_PICO")
add_compile_definitions(CONFIG_OGXM_BOARD_PI_PICO=1)
set(EN_USB_HOST TRUE)
elseif (OGXM_BOARD STREQUAL "PI_PICO2")
add_compile_definitions(CONFIG_OGXM_BOARD_PI_PICO2=1)
set(EN_USB_HOST TRUE)
set(PICO_PLATFORM rp2350)
set(FLASH_SIZE_MB 4)
elseif(OGXM_BOARD STREQUAL "PI_PICOW")
add_compile_definitions(CONFIG_OGXM_BOARD_PI_PICOW=1)
set(EN_BLUETOOTH TRUE)
set(PICO_BOARD pico_w)
elseif(OGXM_BOARD STREQUAL "PI_PICO2W")
add_compile_definitions(CONFIG_OGXM_BOARD_PI_PICO2W=1)
set(EN_BLUETOOTH TRUE)
set(PICO_BOARD pico2_w)
set(PICO_PLATFORM rp2350)
set(FLASH_SIZE_MB 4)
elseif(OGXM_BOARD STREQUAL "ADAFRUIT_FEATHER")
add_compile_definitions(CONFIG_OGXM_BOARD_ADAFRUIT_FEATHER=1)
set(EN_USB_HOST TRUE)
set(EN_RGB TRUE)
set(FLASH_SIZE_MB 8)
elseif(OGXM_BOARD STREQUAL "RP2040_ZERO")
add_compile_definitions(CONFIG_OGXM_BOARD_RP2040_ZERO=1)
set(EN_USB_HOST TRUE)
set(EN_RGB TRUE)
# elseif(OGXM_BOARD STREQUAL "INTERNAL_4CH")
# set(EN_USB_HOST TRUE)
# set(EN_4CH TRUE)
elseif(OGXM_BOARD STREQUAL "EXTERNAL_4CH_I2C")
add_compile_definitions(CONFIG_OGXM_BOARD_EXTERNAL_4CH=1)
set(EN_USB_HOST TRUE)
set(EN_4CH TRUE)
set(EN_RGB TRUE)
elseif(OGXM_BOARD STREQUAL "ESP32_BLUEPAD32_I2C")
add_compile_definitions(CONFIG_OGXM_BOARD_ESP32_BLUEPAD32_I2C=1)
set(EN_ESP32 TRUE)
set(EN_UART_BRIDGE TRUE)
if(OGXM_RETAIL STREQUAL "TRUE")
message(STATUS "Retail mode enabled.")
add_compile_definitions(OGXM_RETAIL=1)
endif()
elseif(OGXM_BOARD STREQUAL "ESP32_BLUERETRO_I2C")
add_compile_definitions(CONFIG_OGXM_BOARD_ESP32_BLUERETRO_I2C=1)
set(EN_ESP32 TRUE)
set(EN_BLUERETRO_I2C TRUE)
set(EN_UART_BRIDGE TRUE)
if(OGXM_RETAIL STREQUAL "TRUE")
message(STATUS "Retail mode enabled.")
add_compile_definitions(OGXM_RETAIL=1)
endif()
else()
message(FATAL_ERROR "Invalid OGXM_BOARD value. See options in src/board_config.h")
endif()
add_definitions(-DOGXM_BOARD=${OGXM_BOARD})
if(EN_USB_HOST)
message(STATUS "USB host enabled.")
add_compile_definitions(CONFIG_EN_USB_HOST=1)
list(APPEND SOURCES_BOARD
${SRC}/USBHost/tuh_callbacks.cpp
# HID
${SRC}/USBHost/HostDriver/DInput/DInput.cpp
${SRC}/USBHost/HostDriver/PSClassic/PSClassic.cpp
${SRC}/USBHost/HostDriver/SwitchWired/SwitchWired.cpp
${SRC}/USBHost/HostDriver/SwitchPro/SwitchPro.cpp
${SRC}/USBHost/HostDriver/PS5/PS5.cpp
${SRC}/USBHost/HostDriver/PS4/PS4.cpp
${SRC}/USBHost/HostDriver/PS3/PS3.cpp
${SRC}/USBHost/HostDriver/N64/N64.cpp
${SRC}/USBHost/HostDriver/HIDGeneric/HIDGeneric.cpp
${SRC}/USBHost/HIDParser/HIDJoystick.cpp
${SRC}/USBHost/HIDParser/HIDReportDescriptor.cpp
${SRC}/USBHost/HIDParser/HIDReportDescriptorElements.cpp
${SRC}/USBHost/HIDParser/HIDReportDescriptorUsages.cpp
${SRC}/USBHost/HIDParser/HIDUtils.cpp
# XInput
${SRC}/USBHost/HostDriver/XInput/XboxOG.cpp
${SRC}/USBHost/HostDriver/XInput/XboxOne.cpp
${SRC}/USBHost/HostDriver/XInput/Xbox360.cpp
${SRC}/USBHost/HostDriver/XInput/Xbox360W.cpp
${SRC}/USBHost/HostDriver/XInput/tuh_xinput/tuh_xinput.cpp
)
list(APPEND LIBS_BOARD
tinyusb_host
tinyusb_pico_pio_usb
)
endif()
if(EN_BLUETOOTH)
add_compile_definitions(CONFIG_EN_BLUETOOTH=1)
message(STATUS "Bluetooth enabled.")
generate_gatt_header(${BTSTACK_ROOT} ${SRC}/BLEServer/att_delayed_response.gatt ${SRC}/BLEServer/att_delayed_response.h)
list(APPEND SOURCES_BOARD
${SRC}/Bluepad32/Bluepad32.cpp
${SRC}/BLEServer/BLEServer.cpp
)
list(APPEND INC_DIRS_BOARD
${SRC}
${BTSTACK_ROOT}/3rd-party/bluedroid/encoder/include
${BTSTACK_ROOT}/3rd-party/bluedroid/decoder/include
)
list(APPEND LIBS_BOARD
pico_cyw43_arch_none
pico_btstack_classic
pico_btstack_cyw43
bluepad32
)
endif()
if(EN_RGB)
add_compile_definitions(CONFIG_EN_RGB=1)
message(STATUS "RGB enabled.")
list(APPEND SOURCES_BOARD
${SRC}/Board/Pico_WS2812/WS2812.cpp
)
list(APPEND LIBS_BOARD
hardware_pio
)
endif()
if(EN_4CH)
add_compile_definitions(CONFIG_EN_4CH=1)
message(STATUS "4CH enabled.")
# list(APPEND SOURCES_BOARD
# ${SRC}/I2CDriver/4Channel/I2CMaster.cpp
# ${SRC}/I2CDriver/4Channel/I2CSlave.cpp
# )
list(APPEND LIBS_BOARD
hardware_i2c
pico_i2c_slave
)
endif()
if(EN_ESP32)
add_compile_definitions(CONFIG_EN_ESP32=1)
message(STATUS "ESP32 enabled.")
if (EN_BLUERETRO_I2C)
# Nothing
else()
list(APPEND LIBS_BOARD
pico_i2c_slave
)
endif()
list(APPEND LIBS_BOARD
hardware_i2c
)
endif()
if(EN_UART_BRIDGE)
add_compile_definitions(CONFIG_EN_UART_BRIDGE=1)
message(STATUS "UART bridge enabled.")
list(APPEND SOURCES_BOARD
${SRC}/USBDevice/DeviceDriver/UARTBridge/UARTBridge.cpp
${SRC}/USBDevice/DeviceDriver/UARTBridge/uart_bridge/uart_bridge.c
)
endif()
string(TIMESTAMP CURRENT_DATETIME "%Y-%m-%d %H:%M:%S")
add_compile_definitions(BUILD_DATETIME="${CURRENT_DATETIME}")
add_compile_definitions(FIRMWARE_NAME="${FW_NAME}")
add_compile_definitions(FIRMWARE_VERSION="${FW_VERSION}")
add_compile_definitions(PICO_FLASH_SIZE_BYTES=${FLASH_SIZE_MB}*1024*1024)
add_compile_definitions(NVS_SECTORS=4)
# Check for DVD dongle firmware
if(EXISTS ${SRC}/USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid_xremote_rom.h)
message(STATUS "XRemote ROM available.")
add_definitions(-DXREMOTE_ROM_AVAILABLE)
endif()
if(NOT EN_BLUETOOTH)
add_compile_definitions(PICO_BOOT_STAGE2_CHOOSE_GENERIC_03H=1)
add_compile_definitions(PICO_XOSC_STARTUP_DELAY_MULTIPLIER=64)
endif()
include(${PICO_SDK_PATH}/pico_sdk_init.cmake)
message("PICO_SDK_VERSION_STRING: ${PICO_SDK_VERSION_STRING}")
if(PICO_SDK_VERSION_STRING VERSION_LESS "${PICOSDK_VERSION_TAG}")
message(FATAL_ERROR
"Raspberry Pi Pico SDK version ${PICOSDK_VERSION_TAG} (or later) required.
Your version is ${PICO_SDK_VERSION_STRING}"
)
endif()
project(${FW_NAME} C CXX ASM)
pico_sdk_init()
add_executable(${FW_NAME} ${SOURCES_BOARD})
set(BUILD_STR "")
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
if(OGXM_BOARD STREQUAL "PI_PICOW")
message(FATAL_ERROR "Debug build will not work with the Pico W currently")
endif()
message(STATUS "Debug build enabled.")
set(BUILD_STR "-Debug")
set(TX_PIN 0)
set(RX_PIN 1)
set(UART_PORT)
include(${CMAKE_CURRENT_LIST_DIR}/cmake/get_pico_uart_port.cmake)
get_pico_uart_port(${TX_PIN} UART_PORT)
message(STATUS "UART port: ${UART_PORT}, TX: ${TX_PIN}, RX: ${RX_PIN}")
pico_enable_stdio_uart(${FW_NAME} 1)
target_compile_definitions(${FW_NAME} PRIVATE
PICO_DEFAULT_UART=${UART_PORT}
PICO_DEFAULT_UART_TX_PIN=${TX_PIN}
PICO_DEFAULT_UART_RX_PIN=${RX_PIN}
)
add_compile_definitions(CFG_TUSB_DEBUG=1)
add_compile_definitions(OGXM_DEBUG=1)
target_compile_options(${FW_NAME} PRIVATE
-Wall # Enable most warnings
-Wextra # Enable extra warnings
-Wconversion # Warn on type conversion issues
-Wsign-conversion # Warn on sign conversion issues
-Wno-unused-parameter # Disable warnings for unused parameters
# -Werror # Treat warnings as errors
)
elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
pico_enable_stdio_uart(${FW_NAME} 0)
add_compile_definitions(CFG_TUSB_DEBUG=0)
add_compile_options(
-O3 # Optimize for speed
# -mcpu=cortex-m0plus # Target ARM Cortex-M0+
-mthumb # Use Thumb instruction set
-ffunction-sections # Place each function in its own section
-fdata-sections # Place each data item in its own section
-fomit-frame-pointer # Omit frame pointers
-finline-functions # Enable inlining
)
add_link_options(
-Wl,--gc-sections # Remove unused sections
-flto # Link-Time Optimization
)
endif()
pico_set_program_name(${FW_NAME} ${FW_NAME})
pico_set_program_version(${FW_NAME} ${FW_VERSION})
target_include_directories(${FW_NAME} PRIVATE
${SRC}
)
if(EN_RGB)
pico_generate_pio_header(${FW_NAME} ${SRC}/Board/Pico_WS2812/WS2812.pio)
endif()
include_directories(${INC_DIRS_BOARD})
if(EN_USB_HOST)
add_subdirectory(${PICO_PIO_USB_PATH} ${CMAKE_BINARY_DIR}/Pico-PIO-USB)
endif()
if(EN_BLUETOOTH)
add_subdirectory(${BLUEPAD32_ROOT}/src/components/bluepad32 libbluepad32)
endif()
add_subdirectory(${LIBFIXMATH_PATH} libfixmath)
target_link_libraries(${FW_NAME} PRIVATE ${LIBS_BOARD})
target_compile_definitions(libfixmath PRIVATE
FIXMATH_FAST_SIN
FIXMATH_NO_64BIT
FIXMATH_NO_CACHE
FIXMATH_NO_HARD_DIVISION
FIXMATH_NO_OVERFLOW
# FIXMATH_NO_ROUNDING
# FIXMATH_OPTIMIZE_8BIT
)
if(OGXM_RETAIL STREQUAL "TRUE")
set(EXE_FILENAME "${FW_NAME}-${FW_VERSION}-${OGXM_BOARD}${BUILD_STR}-Retail")
else()
set(EXE_FILENAME "${FW_NAME}-${FW_VERSION}-${OGXM_BOARD}${BUILD_STR}")
endif()
set_target_properties(${FW_NAME} PROPERTIES OUTPUT_NAME ${EXE_FILENAME})
pico_add_extra_outputs(${FW_NAME})

View File

@@ -0,0 +1,37 @@
function(get_pico_sdk EXTERNAL_DIR VERSION_TAG)
if(NOT DEFINED ENV{PICO_SDK_PATH})
message("PICO_SDK_PATH not set")
set(PICO_SDK_PATH ${EXTERNAL_DIR}/pico-sdk PARENT_SCOPE)
if(NOT EXISTS ${PICO_SDK_PATH})
message("Cloning pico-sdk to ${PICO_SDK_PATH}")
execute_process(
COMMAND git clone --recursive https://github.com/raspberrypi/pico-sdk.git
WORKING_DIRECTORY ${EXTERNAL_DIR}
)
endif()
execute_process(
COMMAND git fetch --tags
WORKING_DIRECTORY ENV{PICO_SDK_PATH}
)
execute_process(
COMMAND git checkout tags/${VERSION_TAG}
WORKING_DIRECTORY ENV{PICO_SDK_PATH}
)
execute_process(
COMMAND git submodule update --init --recursive
WORKING_DIRECTORY ENV{PICO_SDK_PATH}
)
else()
message("Using PICO_SDK_PATH from environment: $ENV{PICO_SDK_PATH}")
set(PICO_SDK_PATH ENV{PICO_SDK_PATH} PARENT_SCOPE)
endif()
set(PICOTOOL_FETCH_FROM_GIT_PATH ${EXTERNAL_DIR}/picotool PARENT_SCOPE)
endfunction()

View File

@@ -0,0 +1,9 @@
function(get_pico_uart_port TX_PIN_IN PICO_UART_PORT_OUT)
if(TX_PIN_IN EQUAL 0 OR TX_PIN_IN EQUAL 12 OR TX_PIN_IN EQUAL 16)
set(${PICO_UART_PORT_OUT} 0 PARENT_SCOPE)
elseif(TX_PIN_IN EQUAL 4 OR TX_PIN_IN EQUAL 8)
set(${PICO_UART_PORT_OUT} 1 PARENT_SCOPE)
else()
message(FATAL_ERROR "Invalid TX pin for Pico UART")
endif()
endfunction()

View File

@@ -0,0 +1,324 @@
#include <cstring>
#include <string>
#include <algorithm>
#include "att_delayed_response.h"
#include "btstack.h"
#include "BLEServer/BLEServer.h"
#include "UserSettings/UserProfile.h"
#include "UserSettings/UserSettings.h"
#include "TaskQueue/TaskQueue.h"
namespace BLEServer {
static constexpr uint16_t PACKET_LEN_MAX = 20;
namespace Handle {
static constexpr uint16_t FW_VERSION = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789020_01_VALUE_HANDLE;
static constexpr uint16_t FW_NAME = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789021_01_VALUE_HANDLE;
static constexpr uint16_t SETUP_READ = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789030_01_VALUE_HANDLE;
static constexpr uint16_t SETUP_WRITE = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789031_01_VALUE_HANDLE;
static constexpr uint16_t GET_SETUP = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789032_01_VALUE_HANDLE;
static constexpr uint16_t PROFILE = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789040_01_VALUE_HANDLE;
static constexpr uint16_t GAMEPAD = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789050_01_VALUE_HANDLE;
}
namespace ADV {
// Flags general discoverable, BR/EDR not supported
static const uint8_t FLAGS[] = { 0x02, 0x01, 0x06 };
static const uint8_t NAME_TYPE = 0x09;
#pragma pack(push, 1)
struct Data {
uint8_t flags[sizeof(FLAGS)];
uint8_t name_len;
uint8_t name_type;
uint8_t name[sizeof(FIRMWARE_NAME) - 1];
Data() {
std::memcpy(flags, FLAGS, sizeof(flags));
name_len = sizeof(FIRMWARE_NAME);
name_type = NAME_TYPE;
std::string fw_name = FIRMWARE_NAME;
std::memcpy(name, fw_name.c_str(), std::min(sizeof(name), fw_name.size()));
}
};
static_assert(sizeof(Data) == 5 + sizeof(FIRMWARE_NAME) - 1, "BLEServer::ADV::Data struct size mismatch");
#pragma pack(pop)
}
#pragma pack(push, 1)
struct SetupPacket {
DeviceDriverType device_type{DeviceDriverType::NONE};
uint8_t max_gamepads{MAX_GAMEPADS};
uint8_t player_idx{0};
uint8_t profile_id{0};
};
static_assert(sizeof(SetupPacket) == 4, "BLEServer::SetupPacket struct size mismatch");
#pragma pack(pop)
class ProfileReader {
public:
ProfileReader() = default;
~ProfileReader() = default;
void set_setup_packet(const SetupPacket& setup_packet) {
setup_packet_ = setup_packet;
current_offset_ = 0;
}
const SetupPacket& get_setup_packet() const {
return setup_packet_;
}
uint16_t get_xfer_len() {
return static_cast<uint16_t>(std::min(static_cast<size_t>(PACKET_LEN_MAX), sizeof(UserProfile) - current_offset_));
}
uint16_t get_profile_data(uint8_t* buffer, uint16_t buffer_len) {
size_t copy_len = get_xfer_len();
if (!buffer || buffer_len < copy_len) {
return 0;
}
if (current_offset_ == 0 && !set_profile()) {
return 0;
}
std::memcpy(buffer, reinterpret_cast<uint8_t*>(&profile_) + current_offset_, copy_len);
current_offset_ += copy_len;
if (current_offset_ >= sizeof(UserProfile)) {
current_offset_ = 0;
}
return copy_len;
}
private:
SetupPacket setup_packet_;
UserProfile profile_;
size_t current_offset_ = 0;
bool set_profile() {
if (setup_packet_.profile_id == 0xFF) {
if (setup_packet_.player_idx >= UserSettings::MAX_PROFILES) {
return false;
}
profile_ = UserSettings::get_instance().get_profile_by_index(setup_packet_.player_idx);
} else {
if (setup_packet_.profile_id > UserSettings::MAX_PROFILES) {
return false;
}
profile_ = UserSettings::get_instance().get_profile_by_id(setup_packet_.profile_id);
}
return true;
}
};
class ProfileWriter
{
public:
ProfileWriter() = default;
~ProfileWriter() = default;
void set_setup_packet(const SetupPacket& setup_packet) {
setup_packet_ = setup_packet;
current_offset_ = 0;
}
const SetupPacket& get_setup_packet() const {
return setup_packet_;
}
uint16_t get_xfer_len() {
return static_cast<uint16_t>(std::min(static_cast<size_t>(PACKET_LEN_MAX), sizeof(UserProfile) - current_offset_));
}
size_t set_profile_data(const uint8_t* buffer, uint16_t buffer_len) {
size_t copy_len = get_xfer_len();
if (!buffer || buffer_len < copy_len) {
return 0;
}
std::memcpy(reinterpret_cast<uint8_t*>(&profile_) + current_offset_, buffer, copy_len);
current_offset_ += copy_len;
size_t ret = current_offset_;
if (current_offset_ >= sizeof(UserProfile)) {
current_offset_ = 0;
}
return ret;
}
bool commit_profile() {
bool success = false;
if (setup_packet_.device_type != DeviceDriverType::NONE) {
success = TaskQueue::Core0::queue_delayed_task(TaskQueue::Core0::get_new_task_id(), 1000, false,
[driver_type = setup_packet_.device_type, profile = profile_, index = setup_packet_.player_idx]
{
UserSettings::get_instance().store_profile_and_driver_type(driver_type, index, profile);
});
} else {
success = TaskQueue::Core0::queue_delayed_task(TaskQueue::Core0::get_new_task_id(), 1000, false,
[index = setup_packet_.player_idx, profile = profile_]
{
UserSettings::get_instance().store_profile(index, profile);
});
}
return success;
}
private:
SetupPacket setup_packet_;
UserProfile profile_;
size_t current_offset_ = 0;
};
std::array<Gamepad*, MAX_GAMEPADS> gamepads_;
ProfileReader profile_reader_;
ProfileWriter profile_writer_;
static int verify_write(const uint16_t buffer_size, const uint16_t expected_size) {
if (buffer_size != expected_size) {
return ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH;
}
return 0;
}
static void disconnect_client_cb(btstack_timer_source_t *ts) {
hci_con_handle_t connection_handle = *static_cast<hci_con_handle_t*>(ts->context);
hci_send_cmd(&hci_disconnect, connection_handle);
delete static_cast<hci_con_handle_t*>(ts->context);
}
static void queue_disconnect(hci_con_handle_t connection_handle, uint32_t dealy_ms) {
static btstack_timer_source_t disconnect_timer;
hci_con_handle_t* connection_handle_ptr = new hci_con_handle_t(connection_handle);
disconnect_timer.process = disconnect_client_cb;
disconnect_timer.context = connection_handle_ptr;
btstack_run_loop_set_timer(&disconnect_timer, dealy_ms);
btstack_run_loop_add_timer(&disconnect_timer);
}
static uint16_t att_read_callback( hci_con_handle_t connection_handle,
uint16_t att_handle,
uint16_t offset,
uint8_t *buffer,
uint16_t buffer_size) {
std::string fw_version;
std::string fw_name;
Gamepad::PadIn pad_in;
switch (att_handle) {
case Handle::FW_VERSION:
fw_version = FIRMWARE_VERSION;
if (buffer) {
std::memcpy(buffer, reinterpret_cast<const uint8_t*>(fw_version.c_str()), fw_version.size());
}
return static_cast<uint16_t>(fw_version.size());
case Handle::FW_NAME:
fw_name = FIRMWARE_NAME;
if (buffer) {
std::memcpy(buffer, reinterpret_cast<const uint8_t*>(fw_name.c_str()), fw_name.size());;
}
return static_cast<uint16_t>(fw_name.size());
case Handle::GET_SETUP:
if (buffer) {
buffer[0] = static_cast<uint8_t>(UserSettings::get_instance().get_current_driver());
buffer[1] = MAX_GAMEPADS;
buffer[2] = 0;
buffer[3] = UserSettings::get_instance().get_active_profile_id(0);
}
return static_cast<uint16_t>(sizeof(SetupPacket));
case Handle::PROFILE:
if (buffer) {
return profile_reader_.get_profile_data(buffer, buffer_size);
}
return profile_reader_.get_xfer_len();
case Handle::GAMEPAD:
if (buffer) {
pad_in = gamepads_.front()->get_pad_in();
std::memcpy(buffer, &pad_in, sizeof(Gamepad::PadIn));
}
return static_cast<uint16_t>(sizeof(Gamepad::PadIn));
default:
break;
}
return 0;
}
static int att_write_callback( hci_con_handle_t connection_handle,
uint16_t att_handle,
uint16_t transaction_mode,
uint16_t offset,
uint8_t *buffer,
uint16_t buffer_size) {
int ret = 0;
switch (att_handle) {
case Handle::SETUP_READ:
if ((ret = verify_write(buffer_size, sizeof(SetupPacket))) != 0) {
break;
}
profile_reader_.set_setup_packet(*reinterpret_cast<SetupPacket*>(buffer));
break;
case Handle::SETUP_WRITE:
if ((ret = verify_write(buffer_size, sizeof(SetupPacket))) != 0) {
break;
}
profile_writer_.set_setup_packet(*reinterpret_cast<SetupPacket*>(buffer));
break;
case Handle::PROFILE:
if ((ret = verify_write(buffer_size, profile_writer_.get_xfer_len())) != 0) {
break;
}
if (profile_writer_.set_profile_data(buffer, buffer_size) == sizeof(UserProfile)) {
queue_disconnect(connection_handle, 500);
profile_writer_.commit_profile();
}
break;
default:
break;
}
return ret;
}
void init_server(Gamepad(&gamepads)[MAX_GAMEPADS]) {
for (uint8_t i = 0; i < MAX_GAMEPADS; i++) {
gamepads_[i] = &gamepads[i];
}
UserSettings::get_instance().initialize_flash();
// setup ATT server
att_server_init(profile_data, att_read_callback, att_write_callback);
// setup advertisements
uint16_t adv_int_min = 0x0030;
uint16_t adv_int_max = 0x0030;
uint8_t adv_type = 0;
bd_addr_t null_addr;
std::memset(null_addr, 0, sizeof(null_addr));
static ADV::Data adv_data = ADV::Data();
gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00);
gap_advertisements_set_data(static_cast<uint8_t>(sizeof(adv_data)), reinterpret_cast<uint8_t*>(&adv_data));
gap_advertisements_enable(1);
}
} // namespace BLEServer

View File

@@ -0,0 +1,9 @@
#pragma once
#include <cstdint>
#include "Gamepad/Gamepad.h"
namespace BLEServer {
void init_server(Gamepad(&gamepads)[MAX_GAMEPADS]);
}

View File

@@ -0,0 +1,29 @@
PRIMARY_SERVICE, GAP_SERVICE
CHARACTERISTIC, GAP_DEVICE_NAME, READ, "XPad"
PRIMARY_SERVICE, GATT_SERVICE
CHARACTERISTIC, GATT_DATABASE_HASH, READ,
// Handle::PRIMARY_SERVICE
PRIMARY_SERVICE, 12345678-1234-1234-1234-123456789012
// Handle::FW_VERSION
CHARACTERISTIC, 12345678-1234-1234-1234-123456789020, READ | DYNAMIC,
// Handle::FW_NAME
CHARACTERISTIC, 12345678-1234-1234-1234-123456789021, READ | DYNAMIC,
// Handle::SETUP_READ
CHARACTERISTIC, 12345678-1234-1234-1234-123456789030, WRITE | DYNAMIC,
// Handle::SETUP_WRITE
CHARACTERISTIC, 12345678-1234-1234-1234-123456789031, WRITE | DYNAMIC,
// Handle::GET_SETUP
CHARACTERISTIC, 12345678-1234-1234-1234-123456789032, READ | DYNAMIC,
// Handle::PROFILE
CHARACTERISTIC, 12345678-1234-1234-1234-123456789040, READ | WRITE | DYNAMIC,
// Handle::GAMEPAD
CHARACTERISTIC, 12345678-1234-1234-1234-123456789050, READ | WRITE | DYNAMIC,

View File

@@ -0,0 +1,301 @@
#include <atomic>
#include <cstring>
#include <functional>
#include <pico/mutex.h>
#include <pico/cyw43_arch.h>
#include "btstack_run_loop.h"
#include "uni.h"
#include "sdkconfig.h"
#include "Bluepad32/Bluepad32.h"
#include "Board/board_api.h"
#include "Board/ogxm_log.h"
#ifndef CONFIG_BLUEPAD32_PLATFORM_CUSTOM
#error "Pico W must use BLUEPAD32_PLATFORM_CUSTOM"
#endif
static_assert((CONFIG_BLUEPAD32_MAX_DEVICES == MAX_GAMEPADS), "Mismatch between BP32 and Gamepad max devices");
namespace bluepad32 {
static constexpr uint32_t FEEDBACK_TIME_MS = 250;
static constexpr uint32_t LED_CHECK_TIME_MS = 500;
struct BTDevice {
bool connected{false};
Gamepad* gamepad{nullptr};
};
BTDevice bt_devices_[MAX_GAMEPADS];
btstack_timer_source_t feedback_timer_;
btstack_timer_source_t led_timer_;
bool led_timer_set_{false};
bool feedback_timer_set_{false};
bool any_connected()
{
for (auto& device : bt_devices_)
{
if (device.connected)
{
return true;
}
}
return false;
}
//This solves a function pointer/crash issue with bluepad32
void set_rumble(uni_hid_device_t* bp_device, uint16_t length, uint8_t rumble_l, uint8_t rumble_r)
{
switch (bp_device->controller_type)
{
case CONTROLLER_TYPE_XBoxOneController:
uni_hid_parser_xboxone_play_dual_rumble(bp_device, 0, length + 10, rumble_l, rumble_r);
break;
case CONTROLLER_TYPE_AndroidController:
if (bp_device->vendor_id == UNI_HID_PARSER_STADIA_VID && bp_device->product_id == UNI_HID_PARSER_STADIA_PID)
{
uni_hid_parser_stadia_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
}
break;
case CONTROLLER_TYPE_PSMoveController:
uni_hid_parser_psmove_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
break;
case CONTROLLER_TYPE_PS3Controller:
uni_hid_parser_ds3_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
break;
case CONTROLLER_TYPE_PS4Controller:
uni_hid_parser_ds4_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
break;
case CONTROLLER_TYPE_PS5Controller:
uni_hid_parser_ds5_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
break;
case CONTROLLER_TYPE_WiiController:
uni_hid_parser_wii_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
break;
case CONTROLLER_TYPE_SwitchProController:
case CONTROLLER_TYPE_SwitchJoyConRight:
case CONTROLLER_TYPE_SwitchJoyConLeft:
uni_hid_parser_switch_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
break;
default:
break;
}
}
static void send_feedback_cb(btstack_timer_source *ts)
{
uni_hid_device_t* bp_device = nullptr;
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i)
{
if (!bt_devices_[i].connected ||
!(bp_device = uni_hid_device_get_instance_for_idx(i)))
{
continue;
}
Gamepad::PadOut gp_out = bt_devices_[i].gamepad->get_pad_out();
if (gp_out.rumble_l > 0 || gp_out.rumble_r > 0)
{
set_rumble(bp_device, static_cast<uint16_t>(FEEDBACK_TIME_MS), gp_out.rumble_l, gp_out.rumble_r);
}
}
btstack_run_loop_set_timer(ts, FEEDBACK_TIME_MS);
btstack_run_loop_add_timer(ts);
}
static void check_led_cb(btstack_timer_source *ts)
{
static bool led_state = false;
led_state = !led_state;
board_api::set_led(any_connected() ? true : led_state);
btstack_run_loop_set_timer(ts, LED_CHECK_TIME_MS);
btstack_run_loop_add_timer(ts);
}
//BT Driver
static void init(int argc, const char** arg_V) {
}
static void init_complete_cb(void) {
uni_bt_enable_new_connections_unsafe(true);
// uni_bt_del_keys_unsafe();
uni_property_dump_all();
}
static uni_error_t device_discovered_cb(bd_addr_t addr, const char* name, uint16_t cod, uint8_t rssi) {
if (!((cod & UNI_BT_COD_MINOR_MASK) & UNI_BT_COD_MINOR_GAMEPAD)) {
return UNI_ERROR_IGNORE_DEVICE;
}
return UNI_ERROR_SUCCESS;
}
static void device_connected_cb(uni_hid_device_t* device) {
}
static void device_disconnected_cb(uni_hid_device_t* device) {
int idx = uni_hid_device_get_idx_for_instance(device);
if (idx >= MAX_GAMEPADS || idx < 0) {
return;
}
bt_devices_[idx].connected = false;
bt_devices_[idx].gamepad->reset_pad_in();
if (!led_timer_set_ && !any_connected()) {
led_timer_set_ = true;
led_timer_.process = check_led_cb;
led_timer_.context = nullptr;
btstack_run_loop_set_timer(&led_timer_, LED_CHECK_TIME_MS);
btstack_run_loop_add_timer(&led_timer_);
}
if (feedback_timer_set_ && !any_connected()) {
feedback_timer_set_ = false;
btstack_run_loop_remove_timer(&feedback_timer_);
}
}
static uni_error_t device_ready_cb(uni_hid_device_t* device) {
int idx = uni_hid_device_get_idx_for_instance(device);
if (idx >= MAX_GAMEPADS || idx < 0) {
return UNI_ERROR_SUCCESS;
}
bt_devices_[idx].connected = true;
if (led_timer_set_) {
led_timer_set_ = false;
btstack_run_loop_remove_timer(&led_timer_);
board_api::set_led(true);
}
if (!feedback_timer_set_) {
feedback_timer_set_ = true;
feedback_timer_.process = send_feedback_cb;
feedback_timer_.context = nullptr;
btstack_run_loop_set_timer(&feedback_timer_, FEEDBACK_TIME_MS);
btstack_run_loop_add_timer(&feedback_timer_);
}
return UNI_ERROR_SUCCESS;
}
static void oob_event_cb(uni_platform_oob_event_t event, void* data) {
return;
}
static void controller_data_cb(uni_hid_device_t* device, uni_controller_t* controller) {
static uni_gamepad_t prev_uni_gp[MAX_GAMEPADS] = {};
if (controller->klass != UNI_CONTROLLER_CLASS_GAMEPAD){
return;
}
uni_gamepad_t *uni_gp = &controller->gamepad;
int idx = uni_hid_device_get_idx_for_instance(device);
Gamepad* gamepad = bt_devices_[idx].gamepad;
Gamepad::PadIn gp_in;
switch (uni_gp->dpad)
{
case DPAD_UP:
gp_in.dpad = gamepad->MAP_DPAD_UP;
break;
case DPAD_DOWN:
gp_in.dpad = gamepad->MAP_DPAD_DOWN;
break;
case DPAD_LEFT:
gp_in.dpad = gamepad->MAP_DPAD_LEFT;
break;
case DPAD_RIGHT:
gp_in.dpad = gamepad->MAP_DPAD_RIGHT;
break;
case DPAD_UP | DPAD_RIGHT:
gp_in.dpad = gamepad->MAP_DPAD_UP_RIGHT;
break;
case DPAD_DOWN | DPAD_RIGHT:
gp_in.dpad = gamepad->MAP_DPAD_DOWN_RIGHT;
break;
case DPAD_DOWN | DPAD_LEFT:
gp_in.dpad = gamepad->MAP_DPAD_DOWN_LEFT;
break;
case DPAD_UP | DPAD_LEFT:
gp_in.dpad = gamepad->MAP_DPAD_UP_LEFT;
break;
default:
break;
}
if (uni_gp->buttons & BUTTON_A) gp_in.buttons |= gamepad->MAP_BUTTON_A;
if (uni_gp->buttons & BUTTON_B) gp_in.buttons |= gamepad->MAP_BUTTON_B;
if (uni_gp->buttons & BUTTON_X) gp_in.buttons |= gamepad->MAP_BUTTON_X;
if (uni_gp->buttons & BUTTON_Y) gp_in.buttons |= gamepad->MAP_BUTTON_Y;
if (uni_gp->buttons & BUTTON_SHOULDER_L) gp_in.buttons |= gamepad->MAP_BUTTON_LB;
if (uni_gp->buttons & BUTTON_SHOULDER_R) gp_in.buttons |= gamepad->MAP_BUTTON_RB;
if (uni_gp->buttons & BUTTON_THUMB_L) gp_in.buttons |= gamepad->MAP_BUTTON_L3;
if (uni_gp->buttons & BUTTON_THUMB_R) gp_in.buttons |= gamepad->MAP_BUTTON_R3;
if (uni_gp->misc_buttons & MISC_BUTTON_BACK) gp_in.buttons |= gamepad->MAP_BUTTON_BACK;
if (uni_gp->misc_buttons & MISC_BUTTON_START) gp_in.buttons |= gamepad->MAP_BUTTON_START;
if (uni_gp->misc_buttons & MISC_BUTTON_SYSTEM) gp_in.buttons |= gamepad->MAP_BUTTON_SYS;
gp_in.trigger_l = gamepad->scale_trigger_l<10>(static_cast<uint16_t>(uni_gp->brake));
gp_in.trigger_r = gamepad->scale_trigger_r<10>(static_cast<uint16_t>(uni_gp->throttle));
std::tie(gp_in.joystick_lx, gp_in.joystick_ly) = gamepad->scale_joystick_l<10>(uni_gp->axis_x, uni_gp->axis_y);
std::tie(gp_in.joystick_rx, gp_in.joystick_ry) = gamepad->scale_joystick_r<10>(uni_gp->axis_rx, uni_gp->axis_ry);
gamepad->set_pad_in(gp_in);
}
const uni_property_t* get_property_cb(uni_property_idx_t idx)
{
return nullptr;
}
uni_platform* get_driver()
{
static uni_platform driver =
{
.name = "OGXMiniW",
.init = init,
.on_init_complete = init_complete_cb,
.on_device_discovered = device_discovered_cb,
.on_device_connected = device_connected_cb,
.on_device_disconnected = device_disconnected_cb,
.on_device_ready = device_ready_cb,
.on_controller_data = controller_data_cb,
.get_property = get_property_cb,
.on_oob_event = oob_event_cb,
};
return &driver;
}
//Public API
void run_task(Gamepad(&gamepads)[MAX_GAMEPADS])
{
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i)
{
bt_devices_[i].gamepad = &gamepads[i];
}
uni_platform_set_custom(get_driver());
uni_init(0, nullptr);
led_timer_set_ = true;
led_timer_.process = check_led_cb;
led_timer_.context = nullptr;
btstack_run_loop_set_timer(&led_timer_, LED_CHECK_TIME_MS);
btstack_run_loop_add_timer(&led_timer_);
btstack_run_loop_execute();
}
} // namespace bluepad32

View File

@@ -0,0 +1,14 @@
#pragma once
#include <cstdint>
#include <array>
#include "Gamepad/Gamepad.h"
#include "Board/Config.h"
/* NOTE: Everything bluepad32/uni needs to be wrapped
and kept away from tinyusb due to naming conflicts */
namespace bluepad32 {
void run_task(Gamepad(&gamepads)[MAX_GAMEPADS]);
}

View File

@@ -0,0 +1,129 @@
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
#define PI_PICO 0
#define RP2040_ZERO 1
#define ADAFRUIT_FEATHER 2
#define PI_PICOW 3
#define ESP32_BLUEPAD32_I2C 4
#define ESP32_BLUERETRO_I2C 5
#define EXTERNAL_4CH_I2C 6
#define INTERNAL_4CH_I2C 7
#define BOARDS_COUNT 8
#define SYSCLOCK_KHZ 240000
#ifndef MAX_GAMEPADS
#define MAX_GAMEPADS 1
#endif
#if defined(CONFIG_OGXM_BOARD_PI_PICO) || defined(CONFIG_OGXM_BOARD_PI_PICO2)
#define OGXM_BOARD PI_PICO
#define PIO_USB_DP_PIN 0 // DM = 1
#define LED_INDICATOR_PIN 25
#elif defined(CONFIG_OGXM_BOARD_PI_PICOW) || defined(CONFIG_OGXM_BOARD_PI_PICO2W)
#define OGXM_BOARD PI_PICOW
#elif defined(CONFIG_OGXM_BOARD_RP2040_ZERO)
#define OGXM_BOARD RP2040_ZERO
#define RGB_PXL_PIN 16
#define PIO_USB_DP_PIN 10 // DM = 11
#define LED_INDICATOR_PIN 14
#elif defined(CONFIG_OGXM_BOARD_ADAFRUIT_FEATHER)
#define OGXM_BOARD ADAFRUIT_FEATHER
#define RGB_PWR_PIN 20
#define RGB_PXL_PIN 21
#define PIO_USB_DP_PIN 16 // DM = 17
#define LED_INDICATOR_PIN 13
#define VCC_EN_PIN 18
#elif defined(CONFIG_OGXM_BOARD_INTERNAL_4CH)
#define OGXM_BOARD INTERNAL_4CH_I2C
#define PIO_USB_DP_PIN 16 // DM = 17
#define FOUR_CH_ENABLED 1
#define I2C_SDA_PIN 10
#define I2C_SCL_PIN 11
#define SLAVE_ADDR_PIN_1 20
#define SLAVE_ADDR_PIN_2 21
#elif defined(CONFIG_OGXM_BOARD_EXTERNAL_4CH)
#define OGXM_BOARD EXTERNAL_4CH_I2C
#define RGB_PXL_PIN 16
#define FOUR_CH_ENABLED 1
#define PIO_USB_DP_PIN 10 // DM = 11
#define I2C_SDA_PIN 6
#define I2C_SCL_PIN 7
#define SLAVE_ADDR_PIN_1 13
#define SLAVE_ADDR_PIN_2 14
#elif defined(CONFIG_OGXM_BOARD_ESP32_BLUEPAD32_I2C)
#define OGXM_BOARD ESP32_BLUEPAD32_I2C
#define I2C_SDA_PIN 18
#define I2C_SCL_PIN 19
#define UART0_TX_PIN 16
#define UART0_RX_PIN 17
#define MODE_SEL_PIN 21
#define ESP_PROG_PIN 20 // ESP32 IO0
#define ESP_RST_PIN 8 // ESP32 EN
#if MAX_GAMEPADS > 1
#undef MAX_GAMEPADS
#define MAX_GAMEPADS 1
#endif
#elif defined(CONFIG_OGXM_BOARD_ESP32_BLUERETRO_I2C)
#define OGXM_BOARD ESP32_BLUERETRO_I2C
#define I2C_SDA_PIN 18
#define I2C_SCL_PIN 19
#define UART0_TX_PIN 16
#define UART0_RX_PIN 17
#define MODE_SEL_PIN 21
#define ESP_PROG_PIN 20 // ESP32 IO0
#define ESP_RST_PIN 8 // ESP32 EN
#if MAX_GAMEPADS > 1
#undef MAX_GAMEPADS
#define MAX_GAMEPADS 1
#endif
#else
#error "Invalid OGXMini board selected"
#endif
#if defined(CONFIG_OGXM_DEBUG)
//Pins and port are defined in CMakeLists.txt
#define DEBUG_UART_PORT __CONCAT(uart,PICO_DEFAULT_UART)
#endif // defined(CONFIG_OGXM_DEBUG)
#if defined(I2C_SDA_PIN)
#define I2C_BAUDRATE 400 * 1000
#define I2C_PORT ((I2C_SDA_PIN == 2 ) || \
(I2C_SDA_PIN == 6 ) || \
(I2C_SDA_PIN == 10) || \
(I2C_SDA_PIN == 14) || \
(I2C_SDA_PIN == 18) || \
(I2C_SDA_PIN == 26)) ? i2c1 : i2c0
#endif // defined(I2C_SDA_PIN)
#if defined(PIO_USB_DP_PIN)
#define PIO_USB_CONFIG { \
PIO_USB_DP_PIN, \
PIO_USB_TX_DEFAULT, \
PIO_SM_USB_TX_DEFAULT, \
PIO_USB_DMA_TX_DEFAULT, \
PIO_USB_RX_DEFAULT, \
PIO_SM_USB_RX_DEFAULT, \
PIO_SM_USB_EOP_DEFAULT, \
NULL, \
PIO_USB_DEBUG_PIN_NONE, \
PIO_USB_DEBUG_PIN_NONE, \
false, \
PIO_USB_PINOUT_DPDM \
}
#endif // defined(PIO_USB_DP_PIN)
#endif // _BOARD_CONFIG_H_

View File

@@ -0,0 +1,114 @@
/*
Copyright ForsakenNGS 2021
https://github.com/ForsakenNGS/Pico_WS2812
*/
#include "Board/Pico_WS2812/WS2812.hpp"
#include "WS2812.pio.h"
WS2812::WS2812(uint pin, uint length, PIO pio, uint sm) {
initialize(pin, length, pio, sm, NONE, GREEN, RED, BLUE);
}
WS2812::WS2812(uint pin, uint length, PIO pio, uint sm, DataFormat format) {
switch (format) {
case FORMAT_RGB:
initialize(pin, length, pio, sm, NONE, RED, GREEN, BLUE);
break;
case FORMAT_GRB:
initialize(pin, length, pio, sm, NONE, GREEN, RED, BLUE);
break;
case FORMAT_WRGB:
initialize(pin, length, pio, sm, WHITE, RED, GREEN, BLUE);
break;
}
}
WS2812::WS2812(uint pin, uint length, PIO pio, uint sm, DataByte b1, DataByte b2, DataByte b3) {
initialize(pin, length, pio, sm, b1, b1, b2, b3);
}
WS2812::WS2812(uint pin, uint length, PIO pio, uint sm, DataByte b1, DataByte b2, DataByte b3, DataByte b4) {
initialize(pin, length, pio, sm, b1, b2, b3, b4);
}
WS2812::~WS2812() {
}
void WS2812::initialize(uint pin, uint length, PIO pio, uint sm, DataByte b1, DataByte b2, DataByte b3, DataByte b4) {
this->pin = pin;
this->length = length;
this->pio = pio;
this->sm = sm;
this->data = new uint32_t[length];
this->bytes[0] = b1;
this->bytes[1] = b2;
this->bytes[2] = b3;
this->bytes[3] = b4;
uint offset = pio_add_program(pio, &ws2812_program);
uint bits = (b1 == NONE ? 24 : 32);
ws2812_program_init(pio, sm, offset, pin, 800000, bits);
}
uint32_t WS2812::convertData(uint32_t rgbw) {
uint32_t result = 0;
for (uint b = 0; b < 4; b++) {
switch (bytes[b]) {
case RED:
result |= (rgbw & 0xFF);
break;
case GREEN:
result |= (rgbw & 0xFF00) >> 8;
break;
case BLUE:
result |= (rgbw & 0xFF0000) >> 16;
break;
case WHITE:
result |= (rgbw & 0xFF000000) >> 24;
break;
}
result <<= 8;
}
return result;
}
void WS2812::setPixelColor(uint index, uint32_t color) {
if (index < length) {
data[index] = convertData(color);
}
}
void WS2812::setPixelColor(uint index, uint8_t red, uint8_t green, uint8_t blue) {
setPixelColor(index, RGB(red, green, blue));
}
void WS2812::setPixelColor(uint index, uint8_t red, uint8_t green, uint8_t blue, uint8_t white) {
setPixelColor(index, RGBW(red, green, blue, white));
}
void WS2812::fill(uint32_t color) {
fill(color, 0, length);
}
void WS2812::fill(uint32_t color, uint first) {
fill(color, first, length-first);
}
void WS2812::fill(uint32_t color, uint first, uint count) {
uint last = (first + count);
if (last > length) {
last = length;
}
color = convertData(color);
for (uint i = first; i < last; i++) {
data[i] = color;
}
}
void WS2812::show() {
for (uint i = 0; i < length; i++) {
pio_sm_put_blocking(pio, sm, data[i]);
}
}

View File

@@ -0,0 +1,63 @@
/*
Copyright ForsakenNGS 2021
https://github.com/ForsakenNGS/Pico_WS2812
*/
#ifndef WS2812_H
#define WS2812_H
#include <array>
#include <pico/types.h>
#include <hardware/pio.h>
class WS2812 {
public:
enum DataByte {
NONE=0,
RED=1,
GREEN=2,
BLUE=3,
WHITE=4
};
enum DataFormat {
FORMAT_RGB=0,
FORMAT_GRB=1,
FORMAT_WRGB=2
};
WS2812(uint pin, uint length, PIO pio, uint sm);
WS2812(uint pin, uint length, PIO pio, uint sm, DataFormat format);
WS2812(uint pin, uint length, PIO pio, uint sm, DataByte b1, DataByte b2, DataByte b3);
WS2812(uint pin, uint length, PIO pio, uint sm, DataByte b1, DataByte b2, DataByte b3, DataByte b4);
~WS2812();
static uint32_t RGB(uint8_t red, uint8_t green, uint8_t blue) {
return (uint32_t)(blue) << 16 | (uint32_t)(green) << 8 | (uint32_t)(red);
};
static uint32_t RGBW(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) {
return (uint32_t)(white) << 24 | (uint32_t)(blue) << 16 | (uint32_t)(green) << 8 | (uint32_t)(red);
}
void setPixelColor(uint index, uint32_t color);
void setPixelColor(uint index, uint8_t red, uint8_t green, uint8_t blue);
void setPixelColor(uint index, uint8_t red, uint8_t green, uint8_t blue, uint8_t white);
void fill(uint32_t color);
void fill(uint32_t color, uint first);
void fill(uint32_t color, uint first, uint count);
void show();
private:
uint pin;
uint length;
PIO pio;
uint sm;
DataByte bytes[4];
uint32_t *data;
void initialize(uint pin, uint length, PIO pio, uint sm, DataByte b1, DataByte b2, DataByte b3, DataByte b4);
uint32_t convertData(uint32_t rgbw);
};
#endif

View File

@@ -0,0 +1,44 @@
.program ws2812
.side_set 1
.define public T1 2
.define public T2 5
.define public T3 3
.lang_opt python sideset_init = pico.PIO.OUT_HIGH
.lang_opt python out_init = pico.PIO.OUT_HIGH
.lang_opt python out_shiftdir = 1
.wrap_target
bitloop:
out x, 1 side 0 [T3 - 1]
jmp !x send_zero side 1 [T1 - 1]
send_one:
jmp bitloop side 1 [T2 - 1]
send_zero:
nop side 0 [T2 - 1]
.wrap
% c-sdk {
#include "hardware/clocks.h"
static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, uint bits) {
pio_gpio_init(pio, pin);
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
pio_sm_config c = ws2812_program_get_default_config(offset);
sm_config_set_sideset_pins(&c, pin);
sm_config_set_out_shift(&c, false, true, bits);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3;
float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
sm_config_set_clkdiv(&c, div);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
%}

View File

@@ -0,0 +1,107 @@
#include <pico/stdlib.h>
#include <pico/mutex.h>
#include <pico/multicore.h>
#include <hardware/clocks.h>
#include "tusb.h"
#include "Board/Config.h"
#include "Board/board_api.h"
#include "Board/ogxm_log.h"
#include "Board/board_api_private/board_api_private.h"
#include "TaskQueue/TaskQueue.h"
namespace board_api {
mutex_t gpio_mutex_;
bool usb::host_connected() {
if (board_api_usbh::host_connected) {
return board_api_usbh::host_connected();
}
return false;
}
//Only call this from core0
void usb::disconnect_all() {
OGXM_LOG("Disconnecting USB and resetting Core1\n");
TaskQueue::suspend_delayed_tasks();
multicore_reset_core1();
sleep_ms(500);
tud_disconnect();
sleep_ms(500);
}
// If using PicoW, only use this method from the core running btstack and after you've called init_bluetooth
void set_led(bool state) {
mutex_enter_blocking(&gpio_mutex_);
if (board_api_led::set_led) {
board_api_led::set_led(state);
}
if (board_api_bt::set_led) {
board_api_bt::set_led(state);
}
if (board_api_rgb::set_led) {
board_api_rgb::set_led(state ? 0x00 : 0xFF, state ? 0xFF : 0x00, 0x00);
}
mutex_exit(&gpio_mutex_);
}
void reboot() {
#define AIRCR_REG (*((volatile uint32_t *)(0xE000ED0C)))
#define AIRCR_SYSRESETREQ (1 << 2)
#define AIRCR_VECTKEY (0x5FA << 16)
OGXM_LOG("Rebooting\n");
AIRCR_REG = AIRCR_VECTKEY | AIRCR_SYSRESETREQ;
while(1);
}
uint32_t ms_since_boot() {
return to_ms_since_boot(get_absolute_time());
}
//Call after board is initialized
void init_bluetooth() {
if (board_api_bt::init) {
board_api_bt::init();
}
}
//Call on core0 before any other method
void init_board() {
if (!set_sys_clock_khz(SYSCLOCK_KHZ, true)) {
if (!set_sys_clock_khz((SYSCLOCK_KHZ / 2), true)) {
panic("Failed to set sys clock");
}
}
stdio_init_all();
if (!mutex_is_initialized(&gpio_mutex_)) {
mutex_init(&gpio_mutex_);
mutex_enter_blocking(&gpio_mutex_);
if (ogxm_log::init) {
ogxm_log::init();
}
if (board_api_led::init) {
board_api_led::init();
}
if (board_api_rgb::init) {
board_api_rgb::init();
}
if (board_api_usbh::init) {
board_api_usbh::init();
}
mutex_exit(&gpio_mutex_);
}
OGXM_LOG("Board initialized\n");
}
} // namespace board_api

View File

@@ -0,0 +1,20 @@
#ifndef _OGXM_BOARD_API_H_
#define _OGXM_BOARD_API_H_
#include <cstdint>
#include <string>
namespace board_api {
void init_board();
void init_bluetooth();
void reboot();
void set_led(bool state);
uint32_t ms_since_boot();
namespace usb {
bool host_connected();
void disconnect_all();
}
}
#endif // _OGXM_BOARD_API_H_

View File

@@ -0,0 +1,34 @@
#include "Board/Config.h"
#if defined(CONFIG_EN_BLUETOOTH)
#include <atomic>
#include <pico/cyw43_arch.h>
#include "Board/board_api_private/board_api_private.h"
#if defined(CYW43_WL_GPIO_LED_PIN) && defined(LED_INDICATOR_PIN)
static_assert(CYW43_WL_GPIO_LED_PIN != LED_INDICATOR_PIN, "CYW43_WL_GPIO_LED_PIN cannot be the same as LED_INDICATOR_PIN");
#endif
namespace board_api_bt {
std::atomic<bool> inited{false};
void init() {
if (cyw43_arch_init() != 0) {
panic("CYW43 init failed");
} else {
inited.store(true);
}
}
void set_led(bool state) {
if (!inited.load()) {
return;
}
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, state ? 1 : 0);
}
} // namespace board_api_bt
#endif // defined(CONFIG_EN_BLUETOOTH)

View File

@@ -0,0 +1,20 @@
#include "Board/Config.h"
#if defined(LED_INDICATOR_PIN)
#include <hardware/gpio.h>
namespace board_api_led {
void init() {
gpio_init(LED_INDICATOR_PIN);
gpio_set_dir(LED_INDICATOR_PIN, GPIO_OUT);
gpio_put(LED_INDICATOR_PIN, 0);
}
void set_led(bool state) {
gpio_put(LED_INDICATOR_PIN, state ? 1 : 0);
}
} // namespace board_api
#endif // LED_INDICATOR_PIN

View File

@@ -0,0 +1,29 @@
#ifndef BOARD_API_PRIVATE_H
#define BOARD_API_PRIVATE_H
#include <cstdint>
#include <string>
#include <sstream>
#include <iostream>
namespace board_api_bt {
void init() __attribute__((weak));
void set_led(bool state) __attribute__((weak));
}
namespace board_api_led {
void init() __attribute__((weak));
void set_led(bool state) __attribute__((weak));
}
namespace board_api_rgb {
void init() __attribute__((weak));
void set_led(uint8_t r, uint8_t g, uint8_t b) __attribute__((weak));
}
namespace board_api_usbh {
void init() __attribute__((weak));
bool host_connected() __attribute__((weak));
}
#endif // BOARD_API_PRIVATE_H

View File

@@ -0,0 +1,33 @@
#include "Board/Config.h"
#if defined(CONFIG_EN_RGB)
#include <hardware/gpio.h>
#include "Board/Pico_WS2812/WS2812.hpp"
#include "Board/board_api_private/board_api_private.h"
namespace board_api_rgb {
WS2812& get_ws2812() {
static WS2812 ws2812 = WS2812(RGB_PXL_PIN, 1, pio1, 0, WS2812::FORMAT_GRB);
return ws2812;
}
void init() {
#if defined(RGB_PWR_PIN)
gpio_init(RGB_PWR_PIN);
gpio_set_dir(RGB_PWR_PIN, GPIO_OUT);
gpio_put(RGB_PWR_PIN, 1);
#endif
set_led(0xFF, 0, 0);
}
void set_led(uint8_t r, uint8_t g, uint8_t b) {
get_ws2812().setPixelColor(0, WS2812::RGB(r, g, b));
get_ws2812().show();
}
} // namespace board_api_rgb
#endif // defined(CONFIG_EN_RGB)

View File

@@ -0,0 +1,60 @@
#include "Board/Config.h"
#if defined(CONFIG_EN_USB_HOST)
#include <atomic>
#include <hardware/gpio.h>
#include "Board/board_api_private/board_api_private.h"
namespace board_api_usbh {
std::atomic<bool> host_connected_ = false;
void host_pin_isr(uint gpio, uint32_t events) {
gpio_set_irq_enabled(PIO_USB_DP_PIN, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false);
gpio_set_irq_enabled(PIO_USB_DP_PIN + 1, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false);
if (gpio == PIO_USB_DP_PIN || gpio == PIO_USB_DP_PIN + 1) {
uint32_t dp_state = gpio_get(PIO_USB_DP_PIN);
uint32_t dm_state = gpio_get(PIO_USB_DP_PIN + 1);
if (dp_state || dm_state) {
host_connected_.store(true);
} else {
host_connected_.store(false);
gpio_set_irq_enabled(PIO_USB_DP_PIN, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true);
gpio_set_irq_enabled(PIO_USB_DP_PIN + 1, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true);
}
}
}
bool host_connected() {
return host_connected_.load();
}
void init() {
#if defined(VCC_EN_PIN)
gpio_init(VCC_EN_PIN);
gpio_set_dir(VCC_EN_PIN, GPIO_OUT);
gpio_put(VCC_EN_PIN, 1);
#endif
gpio_init(PIO_USB_DP_PIN);
gpio_set_dir(PIO_USB_DP_PIN, GPIO_IN);
gpio_pull_down(PIO_USB_DP_PIN);
gpio_init(PIO_USB_DP_PIN + 1);
gpio_set_dir(PIO_USB_DP_PIN + 1, GPIO_IN);
gpio_pull_down(PIO_USB_DP_PIN + 1);
if (gpio_get(PIO_USB_DP_PIN) || gpio_get(PIO_USB_DP_PIN + 1)) {
host_connected_.store(true);
} else {
gpio_set_irq_enabled_with_callback(PIO_USB_DP_PIN, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &host_pin_isr);
gpio_set_irq_enabled_with_callback(PIO_USB_DP_PIN + 1, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &host_pin_isr);
}
}
} // namespace board_api_usbh
#endif // defined(CONFIG_EN_USB_HOST)

View File

@@ -0,0 +1,50 @@
#include "Board/Config.h"
#if defined(CONFIG_EN_ESP32)
#include <pico/stdlib.h>
#include <hardware/gpio.h>
#include "Board/esp32_api.h"
bool esp32_api::uart_bridge_mode()
{
gpio_pull_up(MODE_SEL_PIN);
return (gpio_get(MODE_SEL_PIN) == 0);
}
void esp32_api::reset()
{
gpio_put(ESP_RST_PIN, 0);
sleep_ms(500);
gpio_put(ESP_RST_PIN, 1);
sleep_ms(250);
}
void esp32_api::enter_programming_mode()
{
gpio_put(ESP_PROG_PIN, 1);
sleep_ms(250);
gpio_put(ESP_PROG_PIN, 0);
sleep_ms(250);
reset();
gpio_put(ESP_PROG_PIN, 1);
}
void esp32_api::init()
{
gpio_init(ESP_PROG_PIN);
gpio_set_dir(ESP_PROG_PIN, GPIO_OUT);
gpio_put(ESP_PROG_PIN, 1);
gpio_init(ESP_RST_PIN);
gpio_set_dir(ESP_RST_PIN, GPIO_OUT);
gpio_put(ESP_RST_PIN, 1);
gpio_init(MODE_SEL_PIN);
gpio_set_dir(MODE_SEL_PIN, GPIO_IN);
gpio_pull_up(MODE_SEL_PIN);
}
#endif // defined(CONFIG_EN_ESP32)

View File

@@ -0,0 +1,13 @@
#include "Board/Config.h"
#if defined(CONFIG_EN_ESP32)
#include <cstdint>
namespace esp32_api {
bool uart_bridge_mode();
void reset();
void enter_programming_mode();
void init();
} // namespace board_api_esp32
#endif // defined(CONFIG_EN_ESP32)

View File

@@ -0,0 +1,89 @@
#include "Board/Config.h"
#if defined(CONFIG_OGXM_DEBUG)
#include <cstdint>
#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <pico/mutex.h>
#include <hardware/uart.h>
#include <hardware/gpio.h>
#include "USBDevice/DeviceDriver/DeviceDriverTypes.h"
#include "Board/ogxm_log.h"
std::ostream& operator<<(std::ostream& os, DeviceDriverType type) {
switch (type) {
case DeviceDriverType::NONE: os << "NONE"; break;
case DeviceDriverType::XBOXOG: os << "XBOXOG"; break;
case DeviceDriverType::XBOXOG_SB: os << "XBOXOG_SB"; break;
case DeviceDriverType::XBOXOG_XR: os << "XBOXOG_XR"; break;
case DeviceDriverType::XINPUT: os << "XINPUT"; break;
case DeviceDriverType::PS3: os << "PS3"; break;
case DeviceDriverType::DINPUT: os << "DINPUT"; break;
case DeviceDriverType::PSCLASSIC: os << "PSCLASSIC"; break;
case DeviceDriverType::SWITCH: os << "SWITCH"; break;
case DeviceDriverType::WEBAPP: os << "WEBAPP"; break;
case DeviceDriverType::UART_BRIDGE: os << "UART_BRIDGE"; break;
default: os << "UNKNOWN"; break;
}
return os;
}
namespace ogxm_log {
void init() {
uart_init(DEBUG_UART_PORT, PICO_DEFAULT_UART_BAUD_RATE);
gpio_set_function(PICO_DEFAULT_UART_TX_PIN, GPIO_FUNC_UART);
gpio_set_function(PICO_DEFAULT_UART_RX_PIN, GPIO_FUNC_UART);
}
void log(const std::string& message) {
static mutex_t log_mutex;
if (!mutex_is_initialized(&log_mutex)) {
mutex_init(&log_mutex);
}
mutex_enter_blocking(&log_mutex);
std::string formatted_msg = "OGXM: " + message;
uart_puts(DEBUG_UART_PORT, formatted_msg.c_str());
mutex_exit(&log_mutex);
}
void log(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
char buffer[256];
vsnprintf(buffer, sizeof(buffer), fmt, args);
std::string formatted_msg = std::string(buffer);
log(formatted_msg);
va_end(args);
}
void log_hex(const uint8_t* data, size_t size) {
std::ostringstream hex_stream;
hex_stream << std::hex << std::setfill('0');
int count = 0;
for (uint16_t i = 0; i < size; ++i) {
hex_stream << std::setw(2) << static_cast<int>(data[i]) << " ";
if (++count == 16) {
hex_stream << "\n";
count = 0;
}
}
hex_stream << "\n";
log(hex_stream.str());
}
} // namespace ogxm_log
#endif // defined(CONFIG_OGXM_DEBUG)

View File

@@ -0,0 +1,55 @@
#ifndef BOARD_API_LOG_H
#define BOARD_API_LOG_H
#include <cstdint>
#include "Board/Config.h"
#if defined(CONFIG_OGXM_DEBUG)
#include <string>
#include <sstream>
#include <iostream>
#include <stdarg.h>
#include "USBDevice/DeviceDriver/DeviceDriverTypes.h"
std::ostream& operator<<(std::ostream& os, DeviceDriverType type);
namespace ogxm_log {
void init() __attribute__((weak));
//Don't use this directly, use the OGXM_LOG macro
void log(const std::string& message);
//Don't use this directly, use the OGXM_LOG macro
void log(const char* fmt, ...);
//Don't use this directly, use the OGXM_LOG_HEX macro
void log_hex(const uint8_t* data, size_t size);
template <typename T>
std::string to_string(const T& value) {
std::ostringstream stream;
stream << value;
return stream.str();
}
}
#define OGXM_LOG ogxm_log::log
#define OGXM_LOG_HEX ogxm_log::log_hex
#define OGXM_ASSERT(x) if (!(x)) { OGXM_LOG("Assertion failed: " #x); while(1); }
#define OGXM_ASSERT_MSG(x, msg) if (!(x)) { OGXM_LOG("Assertion failed: " #x " " msg); while(1); }
#define OGXM_TO_STRING ogxm_log::to_string
#else // CONFIG_OGXM_DEBUG
namespace ogxm_log {
void init() __attribute__((weak));
}
#define OGXM_LOG(...)
#define OGXM_LOG_HEX(...)
#define OGXM_ASSERT(x)
#define OGXM_ASSERT_MSG(x, msg)
#define OGXM_TO_STRING(x)
#endif // CONFIG_OGXM_DEBUG
#endif // BOARD_API_LOG_H

View File

@@ -0,0 +1,66 @@
#ifndef _CDC_DEV_DESCRIPTORS_H_
#define _CDC_DEV_DESCRIPTORS_H_
#include <cstdint>
#include <cstring>
#include "tusb.h"
namespace CDCDesc
{
#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
static const uint16_t VID = 0xCafe;
static const uint16_t PID = (0x4000 | _PID_MAP(CDC, 0));
static const uint16_t USB_VER = 0x0200;
const tusb_desc_device_t DESC_DEVICE =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = USB_VER,
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = VID,
.idProduct = PID,
.bcdDevice = 0x0100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};
enum Itf
{
CDC_0 = 0,
CDC_0_DATA,
ITF_TOTAL
};
static const int CONFIG_LEN = (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN);
static const uint8_t DESC_CONFIG[] =
{
TUD_CONFIG_DESCRIPTOR(1, Itf::ITF_TOTAL, 0, CONFIG_LEN, 0x00, 500),
TUD_CDC_DESCRIPTOR(Itf::CDC_0, 4, 0x80 | (Itf::CDC_0 + 1), 8, (Itf::CDC_0 + 2), 0x80 | (Itf::CDC_0 + 2), 64),
};
static const uint8_t STRING_DESC_LANGUAGE[] = { 0x09, 0x04 };
static const uint8_t STRING_MANUFACTURER[] = "Wired Opposite";
static const uint8_t STRING_PRODUCT[] = "OGX-Mini";
static const uint8_t STRING_INTERFACE[] = "OGX-Mini CDC";
static const uint8_t *DESC_STRING[] =
{
STRING_DESC_LANGUAGE,
STRING_MANUFACTURER,
STRING_PRODUCT,
nullptr, //Serial
STRING_INTERFACE
};
}; // namespace CDCDesc
#endif // _CDC_DEV_DESCRIPTORS_H_

View File

@@ -0,0 +1,298 @@
#pragma once
#include <cstdint>
#include <cstring>
#include "tusb.h"
#include "Board/Config.h"
namespace DInput
{
static constexpr uint8_t DPAD_MASK = 0x0F;
static constexpr uint8_t AXIS_MIN = 0x00;
static constexpr uint8_t AXIS_MID = 0x80;
static constexpr uint8_t AXIS_MAX = 0xFF;
namespace Buttons0
{
static constexpr uint8_t SQUARE = 0x01;
static constexpr uint8_t CROSS = 0x02;
static constexpr uint8_t CIRCLE = 0x04;
static constexpr uint8_t TRIANGLE = 0x08;
static constexpr uint8_t L1 = 0x10;
static constexpr uint8_t R1 = 0x20;
static constexpr uint8_t L2 = 0x40;
static constexpr uint8_t R2 = 0x80;
};
namespace Buttons1
{
static constexpr uint8_t SELECT = 0x01;
static constexpr uint8_t START = 0x02;
static constexpr uint8_t L3 = 0x04;
static constexpr uint8_t R3 = 0x08;
static constexpr uint8_t SYS = 0x10;
static constexpr uint8_t TP = 0x20;
};
namespace DPad
{
static constexpr uint8_t UP = 0x00;
static constexpr uint8_t UP_RIGHT = 0x01;
static constexpr uint8_t RIGHT = 0x02;
static constexpr uint8_t DOWN_RIGHT = 0x03;
static constexpr uint8_t DOWN = 0x04;
static constexpr uint8_t DOWN_LEFT = 0x05;
static constexpr uint8_t LEFT = 0x06;
static constexpr uint8_t UP_LEFT = 0x07;
static constexpr uint8_t CENTER = 0x08;
};
#pragma pack(push, 1)
struct InReport
{
uint8_t buttons[2];
uint8_t dpad;
uint8_t joystick_lx;
uint8_t joystick_ly;
uint8_t joystick_rx;
uint8_t joystick_ry;
uint8_t right_axis;
uint8_t left_axis;
uint8_t up_axis;
uint8_t down_axis;
uint8_t triangle_axis;
uint8_t circle_axis;
uint8_t cross_axis;
uint8_t square_axis;
uint8_t l1_axis;
uint8_t r1_axis;
uint8_t l2_axis;
uint8_t r2_axis;
InReport()
{
std::memset(this, 0, sizeof(InReport));
}
};
#pragma pack(pop)
static_assert(sizeof(InReport) == 19, "DInput::InReport is misaligned");
static const uint8_t STRING_DESC_LANGUAGE[] = { 0x09, 0x04 };
static const uint8_t STRING_MANUFACTURER[] = "SHANWAN";
static const uint8_t STRING_PRODUCT[] = "2In1 USB Joystick";
static const uint8_t STRING_VERSION[] = "1.0";
static const uint8_t *STRING_DESCRIPTORS[] __attribute__((unused)) =
{
STRING_DESC_LANGUAGE,
STRING_MANUFACTURER,
STRING_PRODUCT,
STRING_VERSION
};
static const uint8_t DEVICE_DESCRIPTORS[] =
{
0x12, // bLength
0x01, // bDescriptorType (Device)
0x10, 0x01, // bcdUSB 1.10
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
0x40, // bMaxPacketSize0 64
0x63, 0x25, // idVendor 0x2563
0x75, 0x05, // idProduct 0x0575
0x00, 0x02, // bcdDevice 4.00
0x01, // iManufacturer (String Index)
0x02, // iProduct (String Index)
0x00, // iSerialNumber (String Index)
0x01, // bNumConfigurations 1
};
static const uint8_t REPORT_DESCRIPTORS[] =
{
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x05, // Usage (Game Pad)
0xA1, 0x01, // Collection (Application)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x35, 0x00, // Physical Minimum (0)
0x45, 0x01, // Physical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x0D, // Report Count (13)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x0D, // Usage Maximum (0x0D)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x03, // Report Count (3)
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x25, 0x07, // Logical Maximum (7)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
0x09, 0x39, // Usage (Hat switch)
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
0x65, 0x00, // Unit (None)
0x95, 0x01, // Report Count (1)
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x46, 0xFF, 0x00, // Physical Maximum (255)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x32, // Usage (Z)
0x09, 0x35, // Usage (Rz)
0x75, 0x08, // Report Size (8)
0x95, 0x04, // Report Count (4)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x09, 0x20, // Usage (0x20)
0x09, 0x21, // Usage (0x21)
0x09, 0x22, // Usage (0x22)
0x09, 0x23, // Usage (0x23)
0x09, 0x24, // Usage (0x24)
0x09, 0x25, // Usage (0x25)
0x09, 0x26, // Usage (0x26)
0x09, 0x27, // Usage (0x27)
0x09, 0x28, // Usage (0x28)
0x09, 0x29, // Usage (0x29)
0x09, 0x2A, // Usage (0x2A)
0x09, 0x2B, // Usage (0x2B)
0x95, 0x0C, // Report Count (12)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x0A, 0x21, 0x26, // Usage (0x2621)
0x95, 0x08, // Report Count (8)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x0A, 0x21, 0x26, // Usage (0x2621)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x26, 0xFF, 0x03, // Logical Maximum (1023)
0x46, 0xFF, 0x03, // Physical Maximum (1023)
0x09, 0x2C, // Usage (0x2C)
0x09, 0x2D, // Usage (0x2D)
0x09, 0x2E, // Usage (0x2E)
0x09, 0x2F, // Usage (0x2F)
0x75, 0x10, // Report Size (16)
0x95, 0x04, // Report Count (4)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
};
// uint8_t const CONFIGURATION_DESCRIPTORS[] =
// {
// 0x09, // bLength
// 0x02, // bDescriptorType (Configuration)
// 0x29, 0x00, // wTotalLength 41
// 0x01, // bNumInterfaces 1
// 0x01, // bConfigurationValue
// 0x00, // iConfiguration (String Index)
// 0x80, // bmAttributes
// 0xFA, // bMaxPower 500mA
// 0x09, // bLength
// 0x04, // bDescriptorType (Interface)
// 0x00, // bInterfaceNumber 0
// 0x00, // bAlternateSetting
// 0x02, // bNumEndpoints 2
// 0x03, // bInterfaceClass
// 0x00, // bInterfaceSubClass
// 0x00, // bInterfaceProtocol
// 0x00, // iInterface (String Index)
// 0x09, // bLength
// 0x21, // bDescriptorType (HID)
// 0x10, 0x01, // bcdHID 1.10
// 0x00, // bCountryCode
// 0x01, // bNumDescriptors
// 0x22, // bDescriptorType[0] (HID)
// 0x89, 0x00, // wDescriptorLength[0] 137
// 0x07, // bLength
// 0x05, // bDescriptorType (Endpoint)
// 0x02, // bEndpointAddress (OUT/H2D)
// 0x03, // bmAttributes (Interrupt)
// 0x20, 0x00, // wMaxPacketSize 32
// 0x0A, // bInterval 10 (unit depends on device speed)
// 0x07, // bLength
// 0x05, // bDescriptorType (Endpoint)
// 0x81, // bEndpointAddress (IN/D2H)
// 0x03, // bmAttributes (Interrupt)
// 0x20, 0x00, // wMaxPacketSize 32
// 0x0A, // bInterval 10 (unit depends on device speed)
// };
enum Itf {
NUM_HID1 = 0,
#if MAX_GAMEPADS > 1
NUM_HID2,
#endif
#if MAX_GAMEPADS > 2
NUM_HID3,
#endif
#if MAX_GAMEPADS > 3
NUM_HID4,
#endif
NUM_TOTAL
};
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + (TUD_HID_DESC_LEN * MAX_GAMEPADS))
// #define EPNUM_HID1 0x81
// #define EPNUM_HID2 0x82
// #define EPNUM_HID3 0x83
// #define EPNUM_HID4 0x84
uint8_t const CONFIGURATION_DESCRIPTORS[] =
{
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR( 1,
Itf::NUM_TOTAL,
0,
CONFIG_TOTAL_LEN,
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP,
500),
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
TUD_HID_DESCRIPTOR( Itf::NUM_HID1,
0,
HID_ITF_PROTOCOL_NONE,
sizeof(REPORT_DESCRIPTORS),
(0x80 | (Itf::NUM_HID1 + 1)),
CFG_TUD_HID_EP_BUFSIZE,
1),
#if MAX_GAMEPADS > 1
TUD_HID_DESCRIPTOR( Itf::NUM_HID2,
0,
HID_ITF_PROTOCOL_NONE,
sizeof(REPORT_DESCRIPTORS),
(0x80 | (Itf::NUM_HID2 + 1)),
CFG_TUD_HID_EP_BUFSIZE,
1),
#endif
#if MAX_GAMEPADS > 2
TUD_HID_DESCRIPTOR( Itf::NUM_HID3,
0,
HID_ITF_PROTOCOL_NONE,
sizeof(REPORT_DESCRIPTORS),
(0x80 | (Itf::NUM_HID3 + 1)),
CFG_TUD_HID_EP_BUFSIZE,
1),
#endif
#if MAX_GAMEPADS > 3
TUD_HID_DESCRIPTOR( Itf::NUM_HID4,
0,
HID_ITF_PROTOCOL_NONE,
sizeof(REPORT_DESCRIPTORS),
(0x80 | (Itf::NUM_HID4 + 1)),
CFG_TUD_HID_EP_BUFSIZE,
1)
#endif
};
}; // namespace DInput

View File

@@ -0,0 +1,50 @@
#ifndef _N64_DESCRIPTORS_H_
#define _N64_DESCRIPTORS_H_
#include <cstdint>
namespace N64
{
static constexpr uint16_t DPAD_MASK = 0x0F;
static constexpr uint8_t JOY_MIN = 0x00;
static constexpr uint8_t JOY_MID = 0x80;
static constexpr uint8_t JOY_MAX = 0xFF;
namespace Buttons
{
static constexpr uint16_t DPAD_UP = 0x00;
static constexpr uint16_t DPAD_UP_RIGHT = 0x01;
static constexpr uint16_t DPAD_RIGHT = 0x02;
static constexpr uint16_t DPAD_RIGHT_DOWN = 0x03;
static constexpr uint16_t DPAD_DOWN = 0x04;
static constexpr uint16_t DPAD_DOWN_LEFT = 0x05;
static constexpr uint16_t DPAD_LEFT = 0x06;
static constexpr uint16_t DPAD_LEFT_UP = 0x07;
static constexpr uint16_t DPAD_NONE = 0x08;
static constexpr uint16_t C_UP = (1 << 4);
static constexpr uint16_t C_RIGHT = (1 << 5);
static constexpr uint16_t C_DOWN = (1 << 6);
static constexpr uint16_t C_LEFT = (1 << 7);
static constexpr uint16_t L = (1 << 8);
static constexpr uint16_t R = (1 << 9);
static constexpr uint16_t A = (1 << 10);
static constexpr uint16_t Z = (1 << 11);
static constexpr uint16_t B = (1 << 12);
static constexpr uint16_t START = (1 << 13);
};
#pragma pack(push, 1)
struct InReport
{
uint8_t joystick_x;
uint8_t joystick_y;
uint8_t padding[3];
uint16_t buttons;
};
static_assert(sizeof(InReport) == 7, "N64 InReport size is not correct");
#pragma pack(pop)
}; // namespace N64
#endif // _N64_DESCRIPTORS_H_

View File

@@ -0,0 +1,442 @@
#ifndef _PS3_DESCRIPTORS_H_
#define _PS3_DESCRIPTORS_H_
#include <stdint.h>
#include <cstring>
#include <random>
namespace PS3
{
static constexpr uint8_t MAGIC_BYTES[8] = { 0x21, 0x26, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 };
static constexpr uint8_t JOYSTICK_MID = 0x7F;
static constexpr uint16_t SIXAXIS_MID = 0xFF01;
namespace ReportID
{
static constexpr uint8_t FEATURE_01 = 0x01;
static constexpr uint8_t FEATURE_EF = 0xEF;
static constexpr uint8_t GET_PAIRING_INFO = 0xF2;
static constexpr uint8_t FEATURE_F4 = 0xF4;
static constexpr uint8_t FEATURE_F5 = 0xF5;
static constexpr uint8_t FEATURE_F7 = 0xF7;
static constexpr uint8_t FEATURE_F8 = 0xF8;
};
namespace PlugState
{
static constexpr uint8_t PLUGGED = 0x02;
static constexpr uint8_t UNPLUGGED = 0x03;
};
namespace PowerState
{
static constexpr uint8_t CHARGING = 0xEE;
static constexpr uint8_t NOT_CHARGING = 0xF1;
static constexpr uint8_t SHUTDOWN = 0x01;
static constexpr uint8_t DISCHARGING = 0x02;
static constexpr uint8_t LOW = 0x03;
static constexpr uint8_t HIGH = 0x04;
static constexpr uint8_t FULL = 0x05;
};
namespace RumbleState
{
static constexpr uint8_t WIRED_RUMBLE = 0x10;
static constexpr uint8_t WIRED_NO_RUMBLE = 0x12;
static constexpr uint8_t WIRELESS_RUMBLE = 0x14;
static constexpr uint8_t WIRELESS_NO_RUMBLE = 0x16;
};
namespace Buttons0
{
static constexpr uint8_t SELECT = 0x01;
static constexpr uint8_t L3 = 0x02;
static constexpr uint8_t R3 = 0x04;
static constexpr uint8_t START = 0x08;
static constexpr uint8_t DPAD_UP = 0x10;
static constexpr uint8_t DPAD_RIGHT = 0x20;
static constexpr uint8_t DPAD_DOWN = 0x40;
static constexpr uint8_t DPAD_LEFT = 0x80;
};
namespace Buttons1
{
static constexpr uint8_t L2 = 0x01;
static constexpr uint8_t R2 = 0x02;
static constexpr uint8_t L1 = 0x04;
static constexpr uint8_t R1 = 0x08;
static constexpr uint8_t TRIANGLE = 0x10;
static constexpr uint8_t CIRCLE = 0x20;
static constexpr uint8_t CROSS = 0x40;
static constexpr uint8_t SQUARE = 0x80;
};
namespace Buttons2
{
static constexpr uint8_t SYS = 0x01;
static constexpr uint8_t TP = 0x02;
};
const uint8_t DEFAULT_OUT_REPORT[] =
{
0x01, 0xff, 0x00, 0xff, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0x27, 0x10, 0x00, 0x32,
0xff, 0x27, 0x10, 0x00, 0x32,
0xff, 0x27, 0x10, 0x00, 0x32,
0xff, 0x27, 0x10, 0x00, 0x32,
0x00, 0x00, 0x00, 0x00, 0x00
};
#pragma pack(push, 1)
struct InReport
{
uint8_t report_id;
uint8_t unk0;
uint8_t buttons[3];
uint8_t unk1;
uint8_t joystick_lx;
uint8_t joystick_ly;
uint8_t joystick_rx;
uint8_t joystick_ry;
uint8_t unk2[2];
uint8_t move_power_status;
uint8_t unk3;
uint8_t up_axis;
uint8_t right_axis;
uint8_t down_axis;
uint8_t left_axis;
uint8_t l2_axis;
uint8_t r2_axis;
uint8_t l1_axis;
uint8_t r1_axis;
uint8_t triangle_axis;
uint8_t circle_axis;
uint8_t cross_axis;
uint8_t square_axis;
uint8_t unk4[3];
uint8_t plugged;
uint8_t power_status;
uint8_t rumble_status;
uint8_t reserved5[9];
uint16_t acceler_x;
uint16_t acceler_y;
uint16_t acceler_z;
uint16_t gyro_z;
InReport()
{
std::memset(this, 0, sizeof(InReport));
report_id = 0x01;
joystick_lx = JOYSTICK_MID;
joystick_ly = JOYSTICK_MID;
joystick_rx = JOYSTICK_MID;
joystick_ry = JOYSTICK_MID;
plugged = PlugState::PLUGGED;
power_status = PowerState::FULL;
rumble_status = RumbleState::WIRED_RUMBLE;
acceler_x = acceler_y = acceler_z = gyro_z = SIXAXIS_MID;
}
};
static_assert(sizeof(InReport) == 49, "PS3::InReport size mismatch");
struct OutReport
{
//uint8_t report_id;
uint8_t reserved0;
struct Rumble
{
uint8_t right_duration; /* Right motor duration (0xff means forever) */
uint8_t right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */
uint8_t left_duration; /* Left motor duration (0xff means forever) */
uint8_t left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
} rumble;
uint8_t reserved1[4];
uint8_t leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
struct LEDs
{
uint8_t time_enabled; /* the total time the led is active (0xff means forever) */
uint8_t duty_length; /* how long a cycle is in deciseconds (0 means "really fast") */
uint8_t enabled;
uint8_t duty_off; /* % of duty_length the led is off (0xff means 100%) */
uint8_t duty_on; /* % of duty_length the led is on (0xff mean 100%) */
} leds[4]; /* LEDx at (4 - x) */
struct LEDs unused; /* LED5, not actually soldered */
uint8_t reserved2[13];
OutReport()
{
std::memset(this, 0, sizeof(OutReport));
std::memcpy(this, DEFAULT_OUT_REPORT, sizeof(DEFAULT_OUT_REPORT));
}
};
static_assert(sizeof(OutReport) == 48, "PS3::OutReport size mismatch");
static_assert(sizeof(OutReport) >= sizeof(DEFAULT_OUT_REPORT));
static constexpr uint8_t DEFAULT_BT_INFO_HEADER[] =
{
0xFF, 0xFF,
0x00, 0x20, 0x40, 0xCE, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00
};
struct BTInfo
{
uint8_t reserved0[2];
uint8_t device_address[7]; // leading zero followed by address
uint8_t host_address[7]; // leading zero followed by address
uint8_t reserved1;
BTInfo()
{
std::memcpy(device_address, DEFAULT_BT_INFO_HEADER, sizeof(DEFAULT_BT_INFO_HEADER));
std::mt19937 gen(12345);
std::uniform_int_distribution<uint8_t> dist(0, 0xFF);
for (uint8_t i = 4; i < sizeof(device_address); i++)
{
device_address[i] = dist(gen);
}
for (uint8_t i = 1; i < sizeof(host_address); i++)
{
host_address[i] = dist(gen);
}
}
};
static_assert(sizeof(BTInfo) == 17, "PS3::BTInfo size mismatch");
static_assert(sizeof(BTInfo) >= sizeof(DEFAULT_BT_INFO_HEADER));
#pragma pack(pop)
static const uint8_t STRING_LANGUAGE[] = { 0x09, 0x04 };
static const uint8_t STRING_MANUFACTURER[] = "Sony";
static const uint8_t STRING_PRODUCT[] = "PLAYSTATION(R)3 Controller";
static const uint8_t STRING_VERSION[] = "1.0";
static const uint8_t *STRING_DESCRIPTORS[] __attribute__((unused)) =
{
STRING_LANGUAGE,
STRING_MANUFACTURER,
STRING_PRODUCT,
STRING_VERSION
};
static const uint8_t DEVICE_DESCRIPTORS[] =
{
0x12, // bLength
0x01, // bDescriptorType (Device)
0x00, 0x02, // bcdUSB 2.00
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
0x40, // bMaxPacketSize0 64
0x4C, 0x05, // idVendor 0x054C
0x68, 0x02, // idProduct 0x0268
0x00, 0x01, // bcdDevice 2.00
0x01, // iManufacturer (String Index)
0x02, // iProduct (String Index)
0x00, // iSerialNumber (String Index)
0x01, // bNumConfigurations 1
};
static const uint8_t REPORT_DESCRIPTORS[] =
{
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x04, // Usage (Joystick)
0xA1, 0x01, // Collection (Physical)
0xA1, 0x02, // Collection (Application)
0x85, 0x01, // Report ID (1)
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// NOTE: reserved byte
0x75, 0x01, // Report Size (1)
0x95, 0x13, // Report Count (19)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x35, 0x00, // Physical Minimum (0)
0x45, 0x01, // Physical Maximum (1)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x13, // Usage Maximum (0x13)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x01, // Report Size (1)
0x95, 0x0D, // Report Count (13)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// NOTE: 32 bit integer, where 0:18 are buttons and 19:31 are reserved
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x01, // Usage (Pointer)
0xA1, 0x00, // Collection (Undefined)
0x75, 0x08, // Report Size (8)
0x95, 0x04, // Report Count (4)
0x35, 0x00, // Physical Minimum (0)
0x46, 0xFF, 0x00, // Physical Maximum (255)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x32, // Usage (Z)
0x09, 0x35, // Usage (Rz)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// NOTE: four joysticks
0xC0, // End Collection
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x75, 0x08, // Report Size (8)
0x95, 0x27, // Report Count (39)
0x09, 0x01, // Usage (Pointer)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x08, // Report Size (8)
0x95, 0x30, // Report Count (48)
0x09, 0x01, // Usage (Pointer)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x75, 0x08, // Report Size (8)
0x95, 0x30, // Report Count (48)
0x09, 0x01, // Usage (Pointer)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0xA1, 0x02, // Collection (Application)
0x85, 0x02, // Report ID (2)
0x75, 0x08, // Report Size (8)
0x95, 0x30, // Report Count (48)
0x09, 0x01, // Usage (Pointer)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0xA1, 0x02, // Collection (Application)
0x85, 0xEE, // Report ID (238)
0x75, 0x08, // Report Size (8)
0x95, 0x30, // Report Count (48)
0x09, 0x01, // Usage (Pointer)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0xA1, 0x02, // Collection (Application)
0x85, 0xEF, // Report ID (239)
0x75, 0x08, // Report Size (8)
0x95, 0x30, // Report Count (48)
0x09, 0x01, // Usage (Pointer)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0xC0, // End Collection
};
static const uint8_t CONFIGURATION_DESCRIPTORS[] =
{
0x09, // bLength
0x02, // bDescriptorType (Configuration)
0x29, 0x00, // wTotalLength 41
0x01, // bNumInterfaces 1
0x01, // bConfigurationValue
0x00, // iConfiguration (String Index)
0x80, // bmAttributes
0xFA, // bMaxPower 500mA
0x09, // bLength
0x04, // bDescriptorType (Interface)
0x00, // bInterfaceNumber 0
0x00, // bAlternateSetting
0x02, // bNumEndpoints 2
0x03, // bInterfaceClass
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface (String Index)
0x09, // bLength
0x21, // bDescriptorType (HID)
0x11, 0x01, // bcdHID 1.17
0x00, // bCountryCode
0x01, // bNumDescriptors
0x22, // bDescriptorType[0] (HID)
0x94, 0x00, // wDescriptorLength[0] 148
0x07, // bLength
0x05, // bDescriptorType (Endpoint)
0x02, // bEndpointAddress (OUT/H2D)
0x03, // bmAttributes (Interrupt)
0x40, 0x00, // wMaxPacketSize 64
0x01, // bInterval 1 (unit depends on device speed)
0x07, // bLength
0x05, // bDescriptorType (Endpoint)
0x81, // bEndpointAddress (IN/D2H)
0x03, // bmAttributes (Interrupt)
0x40, 0x00, // wMaxPacketSize 64
0x01, // bInterval 1 (unit depends on device speed)
};
static constexpr uint8_t OUTPUT_0x01[] =
{
0x01, 0x04, 0x00, 0x0b, 0x0c, 0x01, 0x02, 0x18,
0x18, 0x18, 0x18, 0x09, 0x0a, 0x10, 0x11, 0x12,
0x13, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02,
0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x04, 0x04,
0x04, 0x04, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02,
0x07, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// calibration data
static constexpr uint8_t OUTPUT_0xEF[] =
{
0xef, 0x04, 0x00, 0x0b, 0x03, 0x01, 0xa0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff,
0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
};
// unknown
static constexpr uint8_t OUTPUT_0xF5[] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // host address - must match 0xf2
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// unknown
static constexpr uint8_t OUTPUT_0xF7[] =
{
0x02, 0x01, 0xf8, 0x02, 0xe2, 0x01, 0x05, 0xff,
0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// unknown
static constexpr uint8_t OUTPUT_0xF8[] =
{
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
} // namespace PS3
#endif // _PS3_DESCRIPTORS_H_

View File

@@ -0,0 +1,114 @@
#ifndef _PS4_DESCRIPTORS_H_
#define _PS4_DESCRIPTORS_H_
#include <cstdint>
namespace PS4
{
static constexpr uint8_t DPAD_MASK = 0x0F;
static constexpr uint8_t COUNTER_MASK = 0xFC;
static constexpr uint8_t AXIS_MAX = 0xFF;
static constexpr uint8_t AXIS_MIN = 0x00;
static constexpr uint8_t JOYSTICK_MID = 0x80;
namespace Buttons0
{
static constexpr uint8_t DPAD_UP = 0x00;
static constexpr uint8_t DPAD_UP_RIGHT = 0x01;
static constexpr uint8_t DPAD_RIGHT = 0x02;
static constexpr uint8_t DPAD_RIGHT_DOWN = 0x03;
static constexpr uint8_t DPAD_DOWN = 0x04;
static constexpr uint8_t DPAD_DOWN_LEFT = 0x05;
static constexpr uint8_t DPAD_LEFT = 0x06;
static constexpr uint8_t DPAD_LEFT_UP = 0x07;
static constexpr uint8_t DPAD_CENTER = 0x08;
static constexpr uint8_t SQUARE = 0x10;
static constexpr uint8_t CROSS = 0x20;
static constexpr uint8_t CIRCLE = 0x40;
static constexpr uint8_t TRIANGLE = 0x80;
};
namespace Buttons1
{
static constexpr uint8_t L1 = 0x01;
static constexpr uint8_t R1 = 0x02;
static constexpr uint8_t L2 = 0x04;
static constexpr uint8_t R2 = 0x08;
static constexpr uint8_t SHARE = 0x10;
static constexpr uint8_t OPTIONS = 0x20;
static constexpr uint8_t L3 = 0x40;
static constexpr uint8_t R3 = 0x80;
};
namespace Buttons2
{
static constexpr uint8_t PS = 0x01;
static constexpr uint8_t TP = 0x02;
};
#pragma pack(push, 1)
struct InReport
{
uint8_t report_id;
uint8_t joystick_lx;
uint8_t joystick_ly;
uint8_t joystick_rx;
uint8_t joystick_ry;
uint8_t buttons[3];
uint8_t trigger_l;
uint8_t trigger_r;
};
static_assert(sizeof(InReport) == 10);
struct OutReport
{
uint8_t report_id;
uint8_t set_rumble : 1;
uint8_t set_led : 1;
uint8_t set_led_blink : 1;
uint8_t set_ext_write : 1;
uint8_t set_left_volume : 1;
uint8_t set_right_volume : 1;
uint8_t set_mic_volume : 1;
uint8_t set_speaker_volume : 1;
uint8_t set_flags2;
uint8_t reserved;
uint8_t motor_right;
uint8_t motor_left;
uint8_t lightbar_red;
uint8_t lightbar_green;
uint8_t lightbar_blue;
uint8_t lightbar_blink_on;
uint8_t lightbar_blink_off;
uint8_t ext_data[8];
uint8_t volume_left;
uint8_t volume_right;
uint8_t volume_mic;
uint8_t volume_speaker;
uint8_t other[9];
};
static_assert(sizeof(OutReport) == 32);
#pragma pack(pop)
static const uint8_t LED_COLORS[][3] =
{
{ 0x00, 0x00, 0x40 }, // Blue
{ 0x40, 0x00, 0x00 }, // Red
{ 0x00, 0x40, 0x00 }, // Green
{ 0x20, 0x00, 0x20 }, // Pink
};
}; // namespace PS4
#endif // _PS4_DESCRIPTORS_H_

View File

@@ -0,0 +1,150 @@
#ifndef _PS5_DESCRIPTORS_H_
#define _PS5_DESCRIPTORS_H_
#include <cstdint>
#include <cstring>
namespace PS5
{
static constexpr uint8_t DPAD_MASK = 0x0F;
static constexpr uint8_t JOYSTICK_MID = 0x80;
namespace OutReportID
{
static constexpr uint8_t CONTROL = 0x02;
static constexpr uint8_t RUMBLE = 0x05;
};
namespace Buttons0
{
static constexpr uint8_t DPAD_UP = 0x00;
static constexpr uint8_t DPAD_UP_RIGHT = 0x01;
static constexpr uint8_t DPAD_RIGHT = 0x02;
static constexpr uint8_t DPAD_RIGHT_DOWN = 0x03;
static constexpr uint8_t DPAD_DOWN = 0x04;
static constexpr uint8_t DPAD_DOWN_LEFT = 0x05;
static constexpr uint8_t DPAD_LEFT = 0x06;
static constexpr uint8_t DPAD_LEFT_UP = 0x07;
static constexpr uint8_t DPAD_CENTER = 0x08;
static constexpr uint8_t SQUARE = 0x10;
static constexpr uint8_t CROSS = 0x20;
static constexpr uint8_t CIRCLE = 0x40;
static constexpr uint8_t TRIANGLE = 0x80;
};
namespace Buttons1
{
static constexpr uint8_t L1 = 0x01;
static constexpr uint8_t R1 = 0x02;
static constexpr uint8_t L2 = 0x04;
static constexpr uint8_t R2 = 0x08;
static constexpr uint8_t SHARE = 0x10;
static constexpr uint8_t OPTIONS = 0x20;
static constexpr uint8_t L3 = 0x40;
static constexpr uint8_t R3 = 0x80;
};
namespace Buttons2
{
static constexpr uint8_t PS = 0x01;
static constexpr uint8_t TP = 0x02;
static constexpr uint8_t MUTE = 0x04;
};
#pragma pack(push, 1)
struct InReport
{
uint8_t report_id;
uint8_t joystick_lx;
uint8_t joystick_ly;
uint8_t joystick_rx;
uint8_t joystick_ry;
uint8_t trigger_l;
uint8_t trigger_r;
uint8_t seq_number;
uint8_t buttons[4];
// Motion sensors
uint16_t gyro[3]; // Gyroscope data for x, y, z axes
uint16_t accel[3]; // Accelerometer data for x, y, z axes
uint32_t sensor_timestamp; // Timestamp for sensor data
uint8_t reserved0;
struct Touchpad
{
uint32_t counter : 7; // Incremented every time a finger touches the touchpad
uint32_t touching : 1; // Indicates if a finger is currently touching the touchpad
uint32_t x : 12; // X coordinate of the touchpoint
uint32_t y : 12; // Y coordinate of the touchpoint
} points[2]; // Array of touchpoints (up to 2)
// uint32_t touchpad[2];
uint8_t reserved1[12];
uint8_t status; // ?
uint8_t reserved2[10];
InReport()
{
std::memset(this, 0, sizeof(InReport));
}
};
static_assert(sizeof(InReport) == 60, "PS5::InReport is not correct size");
struct OutReport
{
uint8_t report_id;
uint8_t control_flag[2];
/* For DualShock 4 compatibility mode. */
uint8_t motor_right;
uint8_t motor_left;
/* Audio controls */
uint8_t headphone_audio_volume; /* 0-0x7f */
uint8_t speaker_audio_volume; /* 0-255 */
uint8_t internal_microphone_volume; /* 0-0x40 */
uint8_t audio_flags;
uint8_t mute_button_led;
uint8_t power_save_control;
/* right trigger motor */
uint8_t right_trigger_motor_mode;
uint8_t right_trigger_param[10];
/* right trigger motor */
uint8_t left_trigger_motor_mode;
uint8_t left_trigger_param[10];
uint8_t reserved2[4];
uint8_t reduce_motor_power;
uint8_t audio_flags2; /* 3 first bits: speaker pre-gain */
/* LEDs and lightbar */
uint8_t led_control_flag;
uint8_t reserved3[2];
uint8_t pulse_option;
uint8_t led_brightness;
uint8_t player_number;
uint8_t lightbar_red;
uint8_t lightbar_green;
uint8_t lightbar_blue;
OutReport()
{
std::memset(this, 0, sizeof(OutReport));
}
};
static_assert(sizeof(OutReport) == 48);
#pragma pack(pop)
}; // namespace PS5
#endif // _PS5_DESCRIPTORS_H_

View File

@@ -0,0 +1,150 @@
#ifndef _PSCLASSIC_DESCRIPTORS_H_
#define _PSCLASSIC_DESCRIPTORS_H_
#include <stdint.h>
namespace PSClassic
{
static constexpr uint16_t DPAD_MASK = 0x3C00;
static constexpr uint16_t JOTSTICK_MID = 0x7f;
namespace Buttons
{
static constexpr uint16_t TRIANGLE = (1U << 0);
static constexpr uint16_t CIRCLE = (1U << 1);
static constexpr uint16_t CROSS = (1U << 2);
static constexpr uint16_t SQUARE = (1U << 3);
static constexpr uint16_t L2 = (1U << 4);
static constexpr uint16_t R2 = (1U << 5);
static constexpr uint16_t L1 = (1U << 6);
static constexpr uint16_t R1 = (1U << 7);
static constexpr uint16_t SELECT = (1U << 8);
static constexpr uint16_t START = (1U << 9);
static constexpr uint16_t UP_LEFT = 0x0000 & DPAD_MASK;
static constexpr uint16_t UP = 0x0400 & DPAD_MASK;
static constexpr uint16_t UP_RIGHT = 0x0800 & DPAD_MASK;
static constexpr uint16_t LEFT = 0x1000 & DPAD_MASK;
static constexpr uint16_t CENTER = 0x1400 & DPAD_MASK;
static constexpr uint16_t RIGHT = 0x1800 & DPAD_MASK;
static constexpr uint16_t DOWN_LEFT = 0x2000 & DPAD_MASK;
static constexpr uint16_t DOWN = 0x2400 & DPAD_MASK;
static constexpr uint16_t DOWN_RIGHT = 0x2800 & DPAD_MASK;
};
struct InReport
{
uint16_t buttons;
};
static const uint8_t STRING_LANGUAGE[] = { 0x09, 0x04 };
static const uint8_t STRING_MANUFACTURER[] = "Sony Interactive Entertainment";
static const uint8_t STRING_PRODUCT[] = "Controller";
static const uint8_t STRING_VERSION[] = { };
static const uint8_t *STRING_DESCRIPTORS[] __attribute__((unused)) =
{
STRING_LANGUAGE,
STRING_MANUFACTURER,
STRING_PRODUCT,
STRING_VERSION
};
static const uint8_t DEVICE_DESCRIPTORS[] =
{
0x12, // bLength
0x01, // bDescriptorType (Device)
0x00, 0x02, // bcdUSB 2.00
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
0x40, // bMaxPacketSize0 64
0x4C, 0x05, // idVendor 0x054C
0xDA, 0x0C, // idProduct 0x0CDA
0x00, 0x01, // bcdDevice 2.00
0x01, // iManufacturer (String Index)
0x02, // iProduct (String Index)
0x00, // iSerialNumber (String Index)
0x01, // bNumConfigurations 1
};
static const uint8_t HID_DESCRIPTORS[] =
{
0x09, // bLength
0x21, // bDescriptorType (HID)
0x11, 0x01, // bcdHID 1.11
0x00, // bCountryCode
0x01, // bNumDescriptors
0x22, // bDescriptorType[0] (HID)
0x31, 0x00, // wDescriptorLength[0] 49
};
static const uint8_t CONFIGURATION_DESCRIPTORS[] =
{
0x09, // bLength
0x02, // bDescriptorType (Configuration)
0x22, 0x00, // wTotalLength 34
0x01, // bNumInterfaces 1
0x01, // bConfigurationValue
0x00, // iConfiguration (String Index)
0xA0, // bmAttributes Remote Wakeup
0x32, // bMaxPower 100mA
0x09, // bLength
0x04, // bDescriptorType (Interface)
0x00, // bInterfaceNumber 0
0x00, // bAlternateSetting
0x01, // bNumEndpoints 1
0x03, // bInterfaceClass
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface (String Index)
0x09, // bLength
0x21, // bDescriptorType (HID)
0x11, 0x01, // bcdHID 1.11
0x00, // bCountryCode
0x01, // bNumDescriptors
0x22, // bDescriptorType[0] (HID)
0x31, 0x00, // wDescriptorLength[0] 49
0x07, // bLength
0x05, // bDescriptorType (Endpoint)
0x81, // bEndpointAddress (IN/D2H)
0x03, // bmAttributes (Interrupt)
0x40, 0x00, // wMaxPacketSize 64
0x0A, // bInterval 10 (unit depends on device speed)
};
static const uint8_t REPORT_DESCRIPTORS[] =
{
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x05, // Usage (Game Pad)
0xA1, 0x01, // Collection (Application)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x0A, // Report Count (10)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x0A, // Usage Maximum (0x0A)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x02, // Logical Maximum (2)
0x35, 0x00, // Physical Minimum (0)
0x45, 0x02, // Physical Maximum (2)
0x75, 0x02, // Report Size (2)
0x95, 0x02, // Report Count (2)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x01, // Report Size (1)
0x95, 0x02, // Report Count (2)
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
};
}; // namespace PSClassic
#endif // _PSCLASSIC_DESCRIPTORS_H_

View File

@@ -0,0 +1,270 @@
#ifndef SWITCH_PRO_DESCRIPTORS_H_
#define SWITCH_PRO_DESCRIPTORS_H_
#include <stdint.h>
namespace SwitchPro
{
static constexpr uint8_t INFO_CONN_MASK = 0xAB;
static constexpr uint8_t INFO_BATTERY_MASK = 0x0F;
namespace CMD
{
static constexpr uint8_t HID = 0x80;
static constexpr uint8_t RUMBLE_ONLY = 0x10;
static constexpr uint8_t AND_RUMBLE = 0x01;
static constexpr uint8_t LED = 0x30;
static constexpr uint8_t LED_HOME = 0x38;
static constexpr uint8_t GYRO = 0x40;
static constexpr uint8_t MODE = 0x03;
static constexpr uint8_t FULL_REPORT_MODE = 0x30;
static constexpr uint8_t HANDSHAKE = 0x02;
static constexpr uint8_t DISABLE_TIMEOUT = 0x04;
}
namespace Buttons0
{
static constexpr uint8_t Y = 0x01;
static constexpr uint8_t X = 0x02;
static constexpr uint8_t B = 0x04;
static constexpr uint8_t A = 0x08;
static constexpr uint8_t R = 0x40;
static constexpr uint8_t ZR = 0x80;
};
namespace Buttons1
{
static constexpr uint8_t MINUS = 0x01;
static constexpr uint8_t PLUS = 0x02;
static constexpr uint8_t L3 = 0x04;
static constexpr uint8_t R3 = 0x08;
static constexpr uint8_t HOME = 0x10;
static constexpr uint8_t CAPTURE = 0x20;
};
namespace Buttons2
{
static constexpr uint8_t DPAD_DOWN = 0x01;
static constexpr uint8_t DPAD_UP = 0x02;
static constexpr uint8_t DPAD_RIGHT = 0x04;
static constexpr uint8_t DPAD_LEFT = 0x08;
static constexpr uint8_t L = 0x40;
static constexpr uint8_t ZL = 0x80;
};
#pragma pack(push, 1)
struct InReport
{
uint8_t report_id;
uint8_t timer;
// uint8_t connInfo : 4;
// uint8_t battery : 4;
uint8_t info;
uint8_t buttons[3];
// uint16_t leftX : 12;
// uint16_t leftY : 12;
// uint16_t rightX : 12;
// uint16_t rightY : 12;
uint8_t joysticks[6];
uint8_t vibrator;
uint16_t accelerX;
uint16_t accelerY;
uint16_t accelerZ;
uint16_t velocityX;
uint16_t velocityY;
uint16_t velocityZ;
};
static_assert(sizeof(InReport) == 25, "InReport size is not correct");
struct OutReport
{
uint8_t command;
uint8_t sequence_counter;
uint8_t rumble_l[4];
uint8_t rumble_r[4];
uint8_t sub_command;
uint8_t sub_command_args[3];
};
static_assert(sizeof(OutReport) == 14, "OutReport size is not correct");
#pragma pack(pop)
// static const uint8_t switch_pro_string_language[] = { 0x09, 0x04 };
// static const uint8_t switch_pro_string_manufacturer[] = "Nintnedo Co., Ltd.";
// static const uint8_t switch_pro_string_product[] = "Pro Controller";
// static const uint8_t switch_pro_string_version[] = "000000000001";
// static const uint8_t *switch_pro_string_descriptors[] __attribute__((unused)) =
// {
// switch_pro_string_language,
// switch_pro_string_product, // switch these?
// switch_pro_string_manufacturer, // ?
// switch_pro_string_version
// };
// static const uint8_t switch_pro_device_descriptor[] =
// {
// 0x12, // bLength
// 0x01, // bDescriptorType (Device)
// 0x00, 0x02, // bcdUSB 2.00
// 0x00, // bDeviceClass (Use class information in the Interface Descriptors)
// 0x00, // bDeviceSubClass
// 0x00, // bDeviceProtocol
// 0x40, // bMaxPacketSize0 64
// 0x7E, 0x05, // idVendor 0x057E
// 0x09, 0x20, // idProduct 0x2009
// 0x00, 0x02, // bcdDevice 4.00
// 0x01, // iManufacturer (String Index)
// 0x02, // iProduct (String Index)
// 0x03, // iSerialNumber (String Index)
// 0x01, // bNumConfigurations 1
// };
// static const uint8_t switch_pro_configuration_descriptor[] =
// {
// 0x09, // bLength
// 0x02, // bDescriptorType (Configuration)
// 0x29, 0x00, // wTotalLength 41
// 0x01, // bNumInterfaces 1
// 0x01, // bConfigurationValue
// 0x00, // iConfiguration (String Index)
// 0xA0, // bmAttributes Remote Wakeup
// 0xFA, // bMaxPower 500mA
// 0x09, // bLength
// 0x04, // bDescriptorType (Interface)
// 0x00, // bInterfaceNumber 0
// 0x00, // bAlternateSetting
// 0x02, // bNumEndpoints 2
// 0x03, // bInterfaceClass
// 0x00, // bInterfaceSubClass
// 0x00, // bInterfaceProtocol
// 0x00, // iInterface (String Index)
// 0x09, // bLength
// 0x21, // bDescriptorType (HID)
// 0x11, 0x01, // bcdHID 1.11
// 0x00, // bCountryCode
// 0x01, // bNumDescriptors
// 0x22, // bDescriptorType[0] (HID)
// 0xCB, 0x00, // wDescriptorLength[0] 203
// 0x07, // bLength
// 0x05, // bDescriptorType (Endpoint)
// 0x81, // bEndpointAddress (IN/D2H)
// 0x03, // bmAttributes (Interrupt)
// 0x40, 0x00, // wMaxPacketSize 64
// 0x08, // bInterval 8 (unit depends on device speed)
// 0x07, // bLength
// 0x05, // bDescriptorType (Endpoint)
// 0x01, // bEndpointAddress (OUT/H2D)
// 0x03, // bmAttributes (Interrupt)
// 0x40, 0x00, // wMaxPacketSize 64
// 0x08, // bInterval 8 (unit depends on device speed)
// };
// static const uint8_t switch_pro_report_descriptor[] =
// {
// 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
// 0x15, 0x00, // Logical Minimum (0)
// 0x09, 0x04, // Usage (Joystick)
// 0xA1, 0x01, // Collection (Application)
// 0x85, 0x30, // Report ID (48)
// 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
// 0x05, 0x09, // Usage Page (Button)
// 0x19, 0x01, // Usage Minimum (0x01)
// 0x29, 0x0A, // Usage Maximum (0x0A)
// 0x15, 0x00, // Logical Minimum (0)
// 0x25, 0x01, // Logical Maximum (1)
// 0x75, 0x01, // Report Size (1)
// 0x95, 0x0A, // Report Count (10)
// 0x55, 0x00, // Unit Exponent (0)
// 0x65, 0x00, // Unit (None)
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// 0x05, 0x09, // Usage Page (Button)
// 0x19, 0x0B, // Usage Minimum (0x0B)
// 0x29, 0x0E, // Usage Maximum (0x0E)
// 0x15, 0x00, // Logical Minimum (0)
// 0x25, 0x01, // Logical Maximum (1)
// 0x75, 0x01, // Report Size (1)
// 0x95, 0x04, // Report Count (4)
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// 0x75, 0x01, // Report Size (1)
// 0x95, 0x02, // Report Count (2)
// 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// 0x0B, 0x01, 0x00, 0x01, 0x00, // Usage (0x010001)
// 0xA1, 0x00, // Collection (Physical)
// 0x0B, 0x30, 0x00, 0x01, 0x00, // Usage (0x010030)
// 0x0B, 0x31, 0x00, 0x01, 0x00, // Usage (0x010031)
// 0x0B, 0x32, 0x00, 0x01, 0x00, // Usage (0x010032)
// 0x0B, 0x35, 0x00, 0x01, 0x00, // Usage (0x010035)
// 0x15, 0x00, // Logical Minimum (0)
// 0x27, 0xFF, 0xFF, 0x00, 0x00, // Logical Maximum (65534)
// 0x75, 0x10, // Report Size (16)
// 0x95, 0x04, // Report Count (4)
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// 0xC0, // End Collection
// 0x0B, 0x39, 0x00, 0x01, 0x00, // Usage (0x010039)
// 0x15, 0x00, // Logical Minimum (0)
// 0x25, 0x07, // Logical Maximum (7)
// 0x35, 0x00, // Physical Minimum (0)
// 0x46, 0x3B, 0x01, // Physical Maximum (315)
// 0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
// 0x75, 0x04, // Report Size (4)
// 0x95, 0x01, // Report Count (1)
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// 0x05, 0x09, // Usage Page (Button)
// 0x19, 0x0F, // Usage Minimum (0x0F)
// 0x29, 0x12, // Usage Maximum (0x12)
// 0x15, 0x00, // Logical Minimum (0)
// 0x25, 0x01, // Logical Maximum (1)
// 0x75, 0x01, // Report Size (1)
// 0x95, 0x04, // Report Count (4)
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// 0x75, 0x08, // Report Size (8)
// 0x95, 0x34, // Report Count (52)
// 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
// 0x85, 0x21, // Report ID (33)
// 0x09, 0x01, // Usage (0x01)
// 0x75, 0x08, // Report Size (8)
// 0x95, 0x3F, // Report Count (63)
// 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// 0x85, 0x81, // Report ID (-127)
// 0x09, 0x02, // Usage (0x02)
// 0x75, 0x08, // Report Size (8)
// 0x95, 0x3F, // Report Count (63)
// 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// 0x85, 0x01, // Report ID (1)
// 0x09, 0x03, // Usage (0x03)
// 0x75, 0x08, // Report Size (8)
// 0x95, 0x3F, // Report Count (63)
// 0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
// 0x85, 0x10, // Report ID (16)
// 0x09, 0x04, // Usage (0x04)
// 0x75, 0x08, // Report Size (8)
// 0x95, 0x3F, // Report Count (63)
// 0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
// 0x85, 0x80, // Report ID (-128)
// 0x09, 0x05, // Usage (0x05)
// 0x75, 0x08, // Report Size (8)
// 0x95, 0x3F, // Report Count (63)
// 0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
// 0x85, 0x82, // Report ID (-126)
// 0x09, 0x06, // Usage (0x06)
// 0x75, 0x08, // Report Size (8)
// 0x95, 0x3F, // Report Count (63)
// 0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
// 0xC0, // End Collection
// };
}; // namespace SwitchPro
#endif // SWITCH_PRO_DESCRIPTORS_H_

View File

@@ -0,0 +1,258 @@
#ifndef _SWITCH_WIRED_DESCRIPTORS_H_
#define _SWITCH_WIRED_DESCRIPTORS_H_
#include <stdint.h>
#include "tusb.h"
namespace SwitchWired
{
static constexpr uint8_t JOYSTICK_MID = 0x80;
namespace DPad
{
static constexpr uint8_t UP = 0x00;
static constexpr uint8_t UP_RIGHT = 0x01;
static constexpr uint8_t RIGHT = 0x02;
static constexpr uint8_t DOWN_RIGHT = 0x03;
static constexpr uint8_t DOWN = 0x04;
static constexpr uint8_t DOWN_LEFT = 0x05;
static constexpr uint8_t LEFT = 0x06;
static constexpr uint8_t UP_LEFT = 0x07;
static constexpr uint8_t CENTER = 0x08;
};
namespace Buttons
{
static constexpr uint16_t Y = (1U << 0);
static constexpr uint16_t B = (1U << 1);
static constexpr uint16_t A = (1U << 2);
static constexpr uint16_t X = (1U << 3);
static constexpr uint16_t L = (1U << 4);
static constexpr uint16_t R = (1U << 5);
static constexpr uint16_t ZL = (1U << 6);
static constexpr uint16_t ZR = (1U << 7);
static constexpr uint16_t MINUS = (1U << 8);
static constexpr uint16_t PLUS = (1U << 9);
static constexpr uint16_t L3 = (1U << 10);
static constexpr uint16_t R3 = (1U << 11);
static constexpr uint16_t HOME = (1U << 12);
static constexpr uint16_t CAPTURE = (1U << 13);
};
#pragma pack(push, 1)
struct InReport
{
uint16_t buttons{0};
uint8_t dpad{DPad::CENTER};
uint8_t joystick_lx{JOYSTICK_MID};
uint8_t joystick_ly{JOYSTICK_MID};
uint8_t joystick_rx{JOYSTICK_MID};
uint8_t joystick_ry{JOYSTICK_MID};
uint8_t vendor{0};
};
static_assert(sizeof(InReport) == 8, "SwitchWired::InReport is not the correct size");
#pragma pack(pop)
static const uint8_t STRING_LANGUAGE[] = { 0x09, 0x04 };
static const uint8_t STRING_MANUFACTURER[] = "HORI CO.,LTD.";
static const uint8_t STRING_PRODUCT[] = "POKKEN CONTROLLER";
static const uint8_t STRING_VERSION[] = "1.0";
static const uint8_t *STRING_DESCRIPTORS[] __attribute__((unused)) =
{
STRING_LANGUAGE,
STRING_MANUFACTURER,
STRING_PRODUCT,
STRING_VERSION
};
static const uint8_t DEVICE_DESCRIPTORS[] =
{
0x12, // bLength
0x01, // bDescriptorType (Device)
0x00, 0x02, // bcdUSB 2.00
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
0x40, // bMaxPacketSize0 64
0x0D, 0x0F, // idVendor 0x0F0D
0x92, 0x00, // idProduct 0x92
0x00, 0x01, // bcdDevice 2.00
0x01, // iManufacturer (String Index)
0x02, // iProduct (String Index)
0x00, // iSerialNumber (String Index)
0x01, // bNumConfigurations 1
};
static const uint8_t HID_DESCRIPTORS[] =
{
0x09, // bLength
0x21, // bDescriptorType (HID)
0x11, 0x01, // bcdHID 1.11
0x00, // bCountryCode
0x01, // bNumDescriptors
0x22, // bDescriptorType[0] (HID)
0x56, 0x00, // wDescriptorLength[0] 86
};
static const uint8_t REPORT_DESCRIPTORS[] =
{
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x05, // Usage (Game Pad)
0xA1, 0x01, // Collection (Application)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x35, 0x00, // Physical Minimum (0)
0x45, 0x01, // Physical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x10, // Report Count (16)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x10, // Usage Maximum (0x10)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x25, 0x07, // Logical Maximum (7)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
0x09, 0x39, // Usage (Hat switch)
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
0x65, 0x00, // Unit (None)
0x95, 0x01, // Report Count (1)
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x46, 0xFF, 0x00, // Physical Maximum (255)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x32, // Usage (Z)
0x09, 0x35, // Usage (Rz)
0x75, 0x08, // Report Size (8)
0x95, 0x04, // Report Count (4)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x09, 0x20, // Usage (0x20)
0x95, 0x01, // Report Count (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x0A, 0x21, 0x26, // Usage (0x2621)
0x95, 0x08, // Report Count (8)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
};
// static const uint8_t CONFIGURATION_DESCRIPTORS[] =
// {
// 0x09, // bLength
// 0x02, // bDescriptorType (Configuration)
// 0x29, 0x00, // wTotalLength 41
// 0x01, // bNumInterfaces 1
// 0x01, // bConfigurationValue
// 0x00, // iConfiguration (String Index)
// 0x80, // bmAttributes
// 0xFA, // bMaxPower 500mA
// 0x09, // bLength
// 0x04, // bDescriptorType (Interface)
// 0x00, // bInterfaceNumber 0
// 0x00, // bAlternateSetting
// 0x02, // bNumEndpoints 2
// 0x03, // bInterfaceClass
// 0x00, // bInterfaceSubClass
// 0x00, // bInterfaceProtocol
// 0x00, // iInterface (String Index)
// 0x09, // bLength
// 0x21, // bDescriptorType (HID)
// 0x11, 0x01, // bcdHID 1.11
// 0x00, // bCountryCode
// 0x01, // bNumDescriptors
// 0x22, // bDescriptorType[0] (HID)
// 0x56, 0x00, // wDescriptorLength[0] 86
// 0x07, // bLength
// 0x05, // bDescriptorType (Endpoint)
// 0x02, // bEndpointAddress (OUT/H2D)
// 0x03, // bmAttributes (Interrupt)
// 0x40, 0x00, // wMaxPacketSize 64
// 0x01, // bInterval 1 (unit depends on device speed)
// 0x07, // bLength
// 0x05, // bDescriptorType (Endpoint)
// 0x81, // bEndpointAddress (IN/D2H)
// 0x03, // bmAttributes (Interrupt)
// 0x40, 0x00, // wMaxPacketSize 64
// 0x01, // bInterval 1 (unit depends on device speed)
// };
enum
{
ITF_NUM_HID1,
#if MAX_GAMEPADS > 1
ITF_NUM_HID2,
#endif
#if MAX_GAMEPADS > 2
ITF_NUM_HID3,
#endif
#if MAX_GAMEPADS > 3
ITF_NUM_HID4,
#endif
ITF_NUM_TOTAL
};
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + (TUD_HID_DESC_LEN * MAX_GAMEPADS))
#define EPNUM_HID1 0x81
#define EPNUM_HID2 0x82
#define EPNUM_HID3 0x83
#define EPNUM_HID4 0x84
uint8_t const CONFIGURATION_DESCRIPTORS[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR( 1,
ITF_NUM_TOTAL,
0,
CONFIG_TOTAL_LEN,
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP,
500),
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
TUD_HID_DESCRIPTOR( ITF_NUM_HID1,
0,
HID_ITF_PROTOCOL_NONE,
sizeof(REPORT_DESCRIPTORS),
EPNUM_HID1,
CFG_TUD_HID_EP_BUFSIZE,
1),
#if MAX_GAMEPADS > 1
TUD_HID_DESCRIPTOR( ITF_NUM_HID2,
0,
HID_ITF_PROTOCOL_NONE,
sizeof(REPORT_DESCRIPTORS),
EPNUM_HID2,
CFG_TUD_HID_EP_BUFSIZE,
1),
#endif
#if MAX_GAMEPADS > 2
TUD_HID_DESCRIPTOR( ITF_NUM_HID3,
0,
HID_ITF_PROTOCOL_NONE,
sizeof(REPORT_DESCRIPTORS),
EPNUM_HID3,
CFG_TUD_HID_EP_BUFSIZE,
1),
#endif
#if MAX_GAMEPADS > 3
TUD_HID_DESCRIPTOR( ITF_NUM_HID4,
0,
HID_ITF_PROTOCOL_NONE,
sizeof(REPORT_DESCRIPTORS),
EPNUM_HID4,
CFG_TUD_HID_EP_BUFSIZE,
1)
#endif
};
}; // namespace SwitchWired
#endif // _SWITCH_WIRED_DESCRIPTORS_H_

View File

@@ -0,0 +1,255 @@
#ifndef _XINPUT_DESCRIPTORS_H_
#define _XINPUT_DESCRIPTORS_H_
#include <cstdint>
#include <cstring>
namespace XInput
{
static constexpr size_t ENDPOINT_IN_SIZE = 20;
static constexpr size_t ENDPOINT_OUT_SIZE = 32;
namespace Chatpad
{
static constexpr uint8_t CODE_1 = 23 ;
static constexpr uint8_t CODE_2 = 22 ;
static constexpr uint8_t CODE_3 = 21 ;
static constexpr uint8_t CODE_4 = 20 ;
static constexpr uint8_t CODE_5 = 19 ;
static constexpr uint8_t CODE_6 = 18 ;
static constexpr uint8_t CODE_7 = 17 ;
static constexpr uint8_t CODE_8 = 103;
static constexpr uint8_t CODE_9 = 102;
static constexpr uint8_t CODE_0 = 101;
static constexpr uint8_t CODE_Q = 39 ;
static constexpr uint8_t CODE_W = 38 ;
static constexpr uint8_t CODE_E = 37 ;
static constexpr uint8_t CODE_R = 36 ;
static constexpr uint8_t CODE_T = 35 ;
static constexpr uint8_t CODE_Y = 34 ;
static constexpr uint8_t CODE_U = 33 ;
static constexpr uint8_t CODE_I = 118;
static constexpr uint8_t CODE_O = 117;
static constexpr uint8_t CODE_P = 100;
static constexpr uint8_t CODE_A = 55;
static constexpr uint8_t CODE_S = 54;
static constexpr uint8_t CODE_D = 53;
static constexpr uint8_t CODE_F = 52;
static constexpr uint8_t CODE_G = 51;
static constexpr uint8_t CODE_H = 50;
static constexpr uint8_t CODE_J = 49;
static constexpr uint8_t CODE_K = 119;
static constexpr uint8_t CODE_L = 114;
static constexpr uint8_t CODE_COMMA = 98;
static constexpr uint8_t CODE_Z = 70;
static constexpr uint8_t CODE_X = 69;
static constexpr uint8_t CODE_C = 68;
static constexpr uint8_t CODE_V = 67;
static constexpr uint8_t CODE_B = 66;
static constexpr uint8_t CODE_N = 65;
static constexpr uint8_t CODE_M = 82;
static constexpr uint8_t CODE_PERIOD = 83;
static constexpr uint8_t CODE_ENTER = 99;
static constexpr uint8_t CODE_LEFT = 85;
static constexpr uint8_t CODE_SPACE = 84;
static constexpr uint8_t CODE_RIGHT = 81;
static constexpr uint8_t CODE_BACK = 113;
//Offset byte 25
static constexpr uint8_t CODE_SHIFT = 1;
static constexpr uint8_t CODE_GREEN = 2;
static constexpr uint8_t CODE_ORANGE = 4;
static constexpr uint8_t CODE_MESSENGER = 8;
};
namespace OutReportID
{
static constexpr uint8_t RUMBLE = 0x00;
static constexpr uint8_t LED = 0x01;
};
namespace Buttons0
{
static constexpr uint8_t DPAD_UP = (1U << 0);
static constexpr uint8_t DPAD_DOWN = (1U << 1);
static constexpr uint8_t DPAD_LEFT = (1U << 2);
static constexpr uint8_t DPAD_RIGHT = (1U << 3);
static constexpr uint8_t START = (1U << 4);
static constexpr uint8_t BACK = (1U << 5);
static constexpr uint8_t L3 = (1U << 6);
static constexpr uint8_t R3 = (1U << 7);
};
namespace Buttons1
{
static constexpr uint8_t LB = (1U << 0);
static constexpr uint8_t RB = (1U << 1);
static constexpr uint8_t HOME = (1U << 2);
static constexpr uint8_t A = (1U << 4);
static constexpr uint8_t B = (1U << 5);
static constexpr uint8_t X = (1U << 6);
static constexpr uint8_t Y = (1U << 7);
};
#pragma pack(push, 1)
struct InReport
{
uint8_t report_id;
uint8_t report_size;
uint8_t buttons[2];
uint8_t trigger_l;
uint8_t trigger_r;
int16_t joystick_lx;
int16_t joystick_ly;
int16_t joystick_rx;
int16_t joystick_ry;
uint8_t reserved[6];
InReport()
{
std::memset(this, 0, sizeof(InReport));
report_size = sizeof(InReport);
}
};
static_assert(sizeof(InReport) == 20, "XInput::InReport is misaligned");
struct WiredChatpadReport
{
uint8_t report_id;
uint8_t chatpad[3];
WiredChatpadReport()
{
std::memset(this, 0, sizeof(WiredChatpadReport));
}
};
static_assert(sizeof(WiredChatpadReport) == 4, "XInput::WiredChatpadReport is misaligned");
struct InReportWireless
{
uint8_t command[4];
uint8_t report_id;
uint8_t report_size;
uint8_t buttons[2];
uint8_t trigger_l;
uint8_t trigger_r;
int16_t joystick_lx;
int16_t joystick_ly;
int16_t joystick_rx;
int16_t joystick_ry; // 18
uint8_t reserved[6];
uint8_t chatpad_status;
uint8_t chatpad[3];
InReportWireless()
{
std::memset(this, 0, sizeof(InReportWireless));
report_size = sizeof(InReportWireless);
}
};
static_assert(sizeof(InReportWireless) == 28, "XInput::InReportWireless is misaligned");
struct OutReport
{
uint8_t report_id;
uint8_t report_size;
uint8_t led;
uint8_t rumble_l;
uint8_t rumble_r;
uint8_t reserved[3];
OutReport()
{
std::memset(this, 0, sizeof(OutReport));
}
};
static_assert(sizeof(OutReport) == 8, "XInput::OutReport is misaligned");
#pragma pack(pop)
static const uint8_t STRING_LANGUAGE[] = { 0x09, 0x04 };
static const uint8_t STRING_MANUFACTURER[] = "Microsoft";
static const uint8_t STRING_PRODUCT[] = "XInput STANDARD GAMEPAD";
static const uint8_t STRING_VERSION[] = "1.0";
static const uint8_t *DESC_STRING[] __attribute__((unused)) =
{
STRING_LANGUAGE,
STRING_MANUFACTURER,
STRING_PRODUCT,
STRING_VERSION
};
static const uint8_t DESC_DEVICE[] =
{
0x12, // bLength
0x01, // bDescriptorType (Device)
0x00, 0x02, // bcdUSB 2.00
0xFF, // bDeviceClass
0xFF, // bDeviceSubClass
0xFF, // bDeviceProtocol
0x40, // bMaxPacketSize0 64
0x5E, 0x04, // idVendor 0x045E
0x8E, 0x02, // idProduct 0x028E
0x14, 0x01, // bcdDevice 2.14
0x01, // iManufacturer (String Index)
0x02, // iProduct (String Index)
0x03, // iSerialNumber (String Index)
0x01, // bNumConfigurations 1
};
static const uint8_t DESC_CONFIGURATION[] =
{
0x09, // bLength
0x02, // bDescriptorType (Configuration)
0x30, 0x00, // wTotalLength 48
0x01, // bNumInterfaces 1
0x01, // bConfigurationValue
0x00, // iConfiguration (String Index)
0x80, // bmAttributes
0xFA, // bMaxPower 500mA
0x09, // bLength
0x04, // bDescriptorType (Interface)
0x00, // bInterfaceNumber 0
0x00, // bAlternateSetting
0x02, // bNumEndpoints 2
0xFF, // bInterfaceClass
0x5D, // bInterfaceSubClass
0x01, // bInterfaceProtocol
0x00, // iInterface (String Index)
0x10, // bLength
0x21, // bDescriptorType (HID)
// 0x10, 0x01, // bcdHID 1.10
0x00, 0x01, // bcdHID 1.00
0x01, // bCountryCode
0x24, // bNumDescriptors
0x81, // bDescriptorType[0] (Unknown 0x81)
0x14, 0x03, // wDescriptorLength[0] 788
0x00, // bDescriptorType[1] (Unknown 0x00)
0x03, 0x13, // wDescriptorLength[1] 4867
0x01, // bDescriptorType[2] (Unknown 0x02)
0x00, 0x03, // wDescriptorLength[2] 768
0x00, // bDescriptorType[3] (Unknown 0x00)
0x07, // bLength
0x05, // bDescriptorType (Endpoint)
0x81, // bEndpointAddress (IN/D2H)
0x03, // bmAttributes (Interrupt)
0x20, 0x00, // wMaxPacketSize 32
0x01, // bInterval 1 (unit depends on device speed)
0x07, // bLength
0x05, // bDescriptorType (Endpoint)
0x01, // bEndpointAddress (OUT/H2D)
0x03, // bmAttributes (Interrupt)
0x20, 0x00, // wMaxPacketSize 32
0x08, // bInterval 8 (unit depends on device speed)
};
};
#endif // _XINPUT_DESCRIPTORS_H_

View File

@@ -0,0 +1,594 @@
#ifndef _XBOX_OG_DESCRIPTORS_H_
#define _XBOX_OG_DESCRIPTORS_H_
#include <cstdint>
#include "tusb.h"
namespace XboxOG
{
//Control request constants
static constexpr uint8_t GET_DESC_REQ_TYPE = 0xC1;
static constexpr uint8_t GET_DESC_REQ = 0x06;
static constexpr uint16_t GET_DESC_VALUE = 0x4200;
static constexpr uint8_t GET_DESC_SUBTYPE_GP_DUKE = 0x01;
static constexpr uint8_t GET_DESC_SUBTYPE_GP_S = 0x02;
static constexpr uint8_t GET_DESC_SUBTYPE_WHEEL = 0x10;
static constexpr uint8_t GET_DESC_SUBTYPE_STICK = 0x20;
static constexpr uint8_t GET_DESC_SUBTYPE_LIGHTGUN = 0x50;
static constexpr uint8_t GET_CAP_REQ_TYPE = 0xC1;
static constexpr uint8_t GET_CAP_REQ = 0x01;
static constexpr uint16_t GET_CAP_VALUE_IN = 0x0100;
static constexpr uint16_t GET_CAP_VALUE_OUT = 0x0200;
static constexpr uint8_t SET_REPORT_REQ_TYPE = 0x21;
static constexpr uint8_t SET_REPORT_REQ = 0x09;
static constexpr uint16_t SET_REPORT_VALUE = 0x0200;
static constexpr uint8_t GET_REPORT_REQ_TYPE = 0xA1;
static constexpr uint8_t GET_REPORT_REQ = 1;
static constexpr uint16_t GET_REPORT_VALUE = 0x0100;
namespace GP //Duke/S
{
namespace Buttons
{
static constexpr uint8_t DPAD_UP = (1 << 0);
static constexpr uint8_t DPAD_DOWN = (1 << 1);
static constexpr uint8_t DPAD_LEFT = (1 << 2);
static constexpr uint8_t DPAD_RIGHT = (1 << 3);
static constexpr uint8_t START = (1 << 4);
static constexpr uint8_t BACK = (1 << 5);
static constexpr uint8_t L3 = (1 << 6);
static constexpr uint8_t R3 = (1 << 7);
};
#pragma pack(push, 1)
struct InReport
{
uint8_t reserved1;
uint8_t report_len;
uint8_t buttons;
uint8_t reserved2;
uint8_t a;
uint8_t b;
uint8_t x;
uint8_t y;
uint8_t black;
uint8_t white;
uint8_t trigger_l;
uint8_t trigger_r;
int16_t joystick_lx;
int16_t joystick_ly;
int16_t joystick_rx;
int16_t joystick_ry;
};
static_assert(sizeof(InReport) == 20, "XboxOG::InReport is not the correct size");
struct OutReport
{
uint8_t reserved;
uint8_t report_len;
uint16_t rumble_l;
uint16_t rumble_r;
};
static_assert(sizeof(OutReport) == 6, "XboxOG::OutReport is not the correct size");
#pragma pack(pop)
static const uint8_t STRING_LANGUAGE[] = { 0x09, 0x04 };
static const uint8_t STRING_MANUFACTURER[] = "";
static const uint8_t STRING_PRODUCT[] = "";
static const uint8_t STRING_VERSION[] = "1.0";
static const uint8_t *STRING_DESCRIPTORS[] __attribute__((unused)) =
{
STRING_LANGUAGE,
STRING_MANUFACTURER,
STRING_PRODUCT,
STRING_VERSION
};
static const tusb_desc_device_t DEVICE_DESCRIPTORS =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0110,
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0x045E,
.idProduct = 0x0289,
.bcdDevice = 0x0121,
.iManufacturer = 0x00,
.iProduct = 0x00,
.iSerialNumber = 0x00,
.bNumConfigurations = 0x01
};
static constexpr uint8_t INTERFACE_CLASS = 0x58;
static constexpr uint8_t INTERFACE_SUBCLASS = 0x42;
static constexpr uint16_t DRIVER_LEN = 9+7+7;
static constexpr uint16_t CONFIG_DESC_TOTAL_LEN = DRIVER_LEN + TUD_CONFIG_DESC_LEN;
enum Interface
{
NUM_DUKE = 0,
NUM_TOTAL
};
#define TUD_XID_DUKE_DESCRIPTOR(_itfnum, _epout, _epin) \
/* Interface */\
9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, INTERFACE_CLASS, INTERFACE_SUBCLASS, 0x00, 0x00,\
/* Endpoint In */\
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(32), 4, \
/* Endpoint Out */\
7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(32), 4
static const uint8_t CONFIGURATION_DESCRIPTORS[] =
{
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR( 1,
Interface::NUM_TOTAL,
0,
CONFIG_DESC_TOTAL_LEN,
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP,
500),
TUD_XID_DUKE_DESCRIPTOR(Interface::NUM_DUKE,
Interface::NUM_DUKE + 1,
0x80 | (Interface::NUM_DUKE + 1)),
};
static const uint8_t XID_DEVICE_DESCRIPTORS[] =
{
0x10,
0x42,
0x00, 0x01,
0x01,
0x02,
0x14,
0x06,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
static const uint8_t XID_CAPABILITIES_IN[] =
{
0x00,
0x14,
0xFF,
0x00,
0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF
};
static const uint8_t XID_CAPABILITIES_OUT[] =
{
0x00,
0x06,
0xFF, 0xFF, 0xFF, 0xFF
};
}; // GP
namespace SB // Steel Battalion
{
static constexpr uint16_t AIMING_MID = 32768;
static constexpr uint16_t BUTTONS2_TOGGLE_MID = 0xFFFC;
namespace Buttons0
{
static constexpr uint16_t RIGHTJOYMAINWEAPON = 0x0001;
static constexpr uint16_t RIGHTJOYFIRE = 0x0002;
static constexpr uint16_t RIGHTJOYLOCKON = 0x0004;
static constexpr uint16_t EJECT = 0x0008;
static constexpr uint16_t COCKPITHATCH = 0x0010;
static constexpr uint16_t IGNITION = 0x0020;
static constexpr uint16_t START = 0x0040;
static constexpr uint16_t MULTIMONOPENCLOSE = 0x0080;
static constexpr uint16_t MULTIMONMAPZOOMINOUT = 0x0100;
static constexpr uint16_t MULTIMONMODESELECT = 0x0200;
static constexpr uint16_t MULTIMONSUBMONITOR = 0x0400;
static constexpr uint16_t MAINMONZOOMIN = 0x0800;
static constexpr uint16_t MAINMONZOOMOUT = 0x1000;
static constexpr uint16_t FUNCTIONFSS = 0x2000;
static constexpr uint16_t FUNCTIONMANIPULATOR = 0x4000;
static constexpr uint16_t FUNCTIONLINECOLORCHANGE = 0x8000;
};
namespace Buttons1
{
static constexpr uint16_t WASHING = 0x0001;
static constexpr uint16_t EXTINGUISHER = 0x0002;
static constexpr uint16_t CHAFF = 0x0004;
static constexpr uint16_t FUNCTIONTANKDETACH = 0x0008;
static constexpr uint16_t FUNCTIONOVERRIDE = 0x0010;
static constexpr uint16_t FUNCTIONNIGHTSCOPE = 0x0020;
static constexpr uint16_t FUNCTIONF1 = 0x0040;
static constexpr uint16_t FUNCTIONF2 = 0x0080;
static constexpr uint16_t FUNCTIONF3 = 0x0100;
static constexpr uint16_t WEAPONCONMAIN = 0x0200;
static constexpr uint16_t WEAPONCONSUB = 0x0400;
static constexpr uint16_t WEAPONCONMAGAZINE = 0x0800;
static constexpr uint16_t COMM1 = 0x1000;
static constexpr uint16_t COMM2 = 0x2000;
static constexpr uint16_t COMM3 = 0x4000;
static constexpr uint16_t COMM4 = 0x8000;
};
namespace Buttons2
{
static constexpr uint16_t COMM5 = 0x0001;
static constexpr uint16_t LEFTJOYSIGHTCHANGE = 0x0002;
static constexpr uint16_t TOGGLEFILTERCONTROL = 0x0004;
static constexpr uint16_t TOGGLEOXYGENSUPPLY = 0x0008;
static constexpr uint16_t TOGGLEFUELFLOWRATE = 0x0010;
static constexpr uint16_t TOGGLEBUFFREMATERIAL = 0x0020;
static constexpr uint16_t TOGGLEVTLOCATION = 0x0040;
};
namespace Axis
{
static constexpr uint16_t AIMINGX = 0x0001;
static constexpr uint16_t AIMINGY = 0x0002;
static constexpr uint16_t LEVER = 0x0004;
static constexpr uint16_t SIGHTX = 0x0008;
static constexpr uint16_t SIGHTY = 0x0010;
static constexpr uint16_t LPEDAL = 0x0020;
static constexpr uint16_t MPEDAL = 0x0040;
static constexpr uint16_t RPEDAL = 0x0080;
static constexpr uint16_t TUNER = 0x0100;
static constexpr uint16_t GEAR = 0x0200;
};
namespace Gear
{
static constexpr int8_t R = 7;
static constexpr int8_t N = 8;
static constexpr int8_t G1 = 9;
static constexpr int8_t G2 = 10;
static constexpr int8_t G3 = 11;
static constexpr int8_t G4 = 12;
static constexpr int8_t G5 = 13;
};
#pragma pack(push, 1)
struct InReport
{
uint8_t zero;
uint8_t bLength;
uint16_t dButtons[3];
uint16_t aimingX; //0 to 2^16 left to right
uint16_t aimingY; //0 to 2^16 top to bottom
int16_t rotationLever;
int16_t sightChangeX;
int16_t sightChangeY;
uint16_t leftPedal; //Sidestep, 0x0000 to 0xFF00
uint16_t middlePedal; //Brake, 0x0000 to 0xFF00
uint16_t rightPedal; //Acceleration, 0x0000 to oxFF00
int8_t tunerDial; //0-15 is from 9oclock, around clockwise
int8_t gearLever; //7-13 is gears R,1,2,3,4,5
};
static_assert(sizeof(InReport) == 26, "XboxOGSB::InReport is not the correct size");
struct OutReport
{
uint8_t zero;
uint8_t bLength;
uint8_t CockpitHatch_EmergencyEject;
uint8_t Start_Ignition;
uint8_t MapZoomInOut_OpenClose;
uint8_t SubMonitorModeSelect_ModeSelect;
uint8_t MainMonitorZoomOut_MainMonitorZoomIn;
uint8_t Manipulator_ForecastShootingSystem;
uint8_t Washing_LineColorChange;
uint8_t Chaff_Extinguisher;
uint8_t Override_TankDetach;
uint8_t F1_NightScope;
uint8_t F3_F2;
uint8_t SubWeaponControl_MainWeaponControl;
uint8_t Comm1_MagazineChange;
uint8_t Comm3_Comm2;
uint8_t Comm5_Comm4;
uint8_t GearR_;
uint8_t Gear1_GearN;
uint8_t Gear3_Gear2;
uint8_t Gear5_Gear4;
uint8_t dummy;
};
static_assert(sizeof(OutReport) == 22, "XboxOGSB::OutReport is not the correct size");
#pragma pack(pop)
static const uint8_t STRING_LANGUAGE[] = { 0x09, 0x04 };
static const uint8_t STRING_MANUFACTURER[] = "";
static const uint8_t STRING_PRODUCT[] = "";
static const uint8_t STRING_VERSION[] = "1.0";
static const uint8_t *STRING_DESCRIPTORS[] __attribute__((unused)) =
{
STRING_LANGUAGE,
STRING_MANUFACTURER,
STRING_PRODUCT,
STRING_VERSION
};
static constexpr uint8_t INTERFACE_CLASS = 0x58;
static constexpr uint8_t INTERFACE_SUBCLASS = 0x42;
static constexpr uint16_t DRIVER_LEN = 9+7+7;
static constexpr uint16_t CONFIG_DESC_TOTAL_LEN = DRIVER_LEN + TUD_CONFIG_DESC_LEN;
enum Interface
{
NUM_STEELBATTALION = 0,
NUM_TOTAL
};
#define TUD_XID_SB_DESCRIPTOR(_itfnum, _epout, _epin) \
/* Interface */\
9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, INTERFACE_CLASS, INTERFACE_SUBCLASS, 0x00, 0x00,\
/* Endpoint In */\
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(32), 4, \
/* Endpoint Out */\
7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(32), 4
static uint8_t const CONFIGURATION_DESCRIPTORS[] =
{
TUD_CONFIG_DESCRIPTOR( 1,
Interface::NUM_TOTAL,
0,
CONFIG_DESC_TOTAL_LEN,
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP,
500),
TUD_XID_SB_DESCRIPTOR( Interface::NUM_STEELBATTALION,
Interface::NUM_STEELBATTALION + 1,
0x80 | (Interface::NUM_STEELBATTALION + 1)),
};
static const uint8_t XID_DEVICE_DESCRIPTORS[] =
{
0x10,
0x42,
0x00, 0x01,
0x80,
0x01,
0x1A,
0x16,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
static const uint8_t XID_CAPABILITIES_IN[] =
{
0x00,
0x1A,
0xFF,
0xFF,
0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF
};
static const uint8_t XID_CAPABILITIES_OUT[] =
{
0x00,
0x16,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF
};
}; // SB
namespace XR //XRemote
{
#define XID_REMOTE_INTERFACE_CLASS 0x58
#define XID_REMOTE_INTERFACE_SUBCLASS 0x42
#define XID_XREMOTE_ROM_CLASS 0x59
namespace ButtonCode
{
static constexpr uint16_t SELECT = 0x0A0B;
static constexpr uint16_t UP = 0x0AA6;
static constexpr uint16_t DOWN = 0x0AA7;
static constexpr uint16_t RIGHT = 0x0AA8;
static constexpr uint16_t LEFT = 0x0AA9;
static constexpr uint16_t INFO = 0x0AC3;
static constexpr uint16_t NINE = 0x0AC6;
static constexpr uint16_t EIGHT = 0x0AC7;
static constexpr uint16_t SEVEN = 0x0AC8;
static constexpr uint16_t SIX = 0x0AC9;
static constexpr uint16_t FIVE = 0x0ACA;
static constexpr uint16_t FOUR = 0x0ACB;
static constexpr uint16_t THREE = 0x0ACC;
static constexpr uint16_t TWO = 0x0ACD;
static constexpr uint16_t ONE = 0x0ACE;
static constexpr uint16_t ZERO = 0x0ACF;
static constexpr uint16_t DISPLAY = 0x0AD5;
static constexpr uint16_t BACK = 0x0AD8;
static constexpr uint16_t SKIP_MINUS = 0x0ADD;
static constexpr uint16_t SKIP_PLUS = 0x0ADF;
static constexpr uint16_t STOP = 0x0AE0;
static constexpr uint16_t REVERSE = 0x0AE2;
static constexpr uint16_t FORWARD = 0x0AE3;
static constexpr uint16_t TITLE = 0x0AE5;
static constexpr uint16_t PAUSE = 0x0AE6;
static constexpr uint16_t PLAY = 0x0AEA;
static constexpr uint16_t MENU = 0x0AF7;
};
#pragma pack(push, 1)
struct InReport
{
uint8_t zero;
uint8_t bLength;
uint16_t buttonCode;
uint16_t timeElapsed; // ms since last button press
};
static_assert(sizeof(InReport) == 6, "XboxOGXRemote::InReport is not the correct size");
#pragma pack(pop)
static constexpr uint16_t DRIVER_LEN = 9+7+9;
static const uint8_t DEVICE_DESCRIPTORS[] =
{
0x12, // bLength
0x01, // bDescriptorType (Device)
0x10, 0x01, // bcdUSB 1.10
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
0x40, // bMaxPacketSize0 64
0x5E, 0x04, // idVendor 0x045E
0x84, 0x02, // idProduct 0x0284
0x30, 0x01, // bcdDevice 2.30
0x00, // iManufacturer (String Index)
0x00, // iProduct (String Index)
0x00, // iSerialNumber (String Index)
0x01, // bNumConfigurations 1
};
static const uint8_t CONFIGURATION_DESCRIPTORS[] =
{
0x09, // bLength
0x02, // bDescriptorType (Configuration)
0x22, 0x00, // wTotalLength 34
0x02, // bNumInterfaces 2
0x01, // bConfigurationValue
0x00, // iConfiguration (String Index)
0x80, // bmAttributes
0xFA, // bMaxPower 500mA
0x09, // bLength
0x04, // bDescriptorType (Interface)
0x00, // bInterfaceNumber 0
0x00, // bAlternateSetting
0x01, // bNumEndpoints 1
0x58, // bInterfaceClass
0x42, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface (String Index)
0x07, // bLength
0x05, // bDescriptorType (Endpoint)
0x81, // bEndpointAddress (IN/D2H)
0x03, // bmAttributes (Interrupt)
0x08, 0x00, // wMaxPacketSize 8
0x10, // bInterval 16 (unit depends on device speed)
0x09, // bLength
0x04, // bDescriptorType (Interface)
0x01, // bInterfaceNumber 1
0x00, // bAlternateSetting
0x00, // bNumEndpoints 0
0x59, // bInterfaceClass
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface (String Index)
};
static const uint8_t XID_DEVICE_DESCRIPTORS[] =
{
0x08,
0x42,
0x00, 0x01,
0x03,
0x00,
0x06,
0x00
};
}; // XR
namespace COM
{
static constexpr uint8_t INTERFACE_CLASS = 0x78;
static constexpr uint8_t INTERFACE_SUBCLASS = 0x00;
static const uint8_t DEVICE_DESCRIPTORS[] =
{
0x12, // bLength
0x01, // bDescriptorType (Device)
0x10, 0x01, // bcdUSB 1.10
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
0x08, // bMaxPacketSize0 8
0x5E, 0x04, // idVendor 0x045E
0x83, 0x02, // idProduct 0x0283
0x58, 0x01, // bcdDevice 2.58
0x00, // iManufacturer (String Index)
0x00, // iProduct (String Index)
0x00, // iSerialNumber (String Index)
0x01, // bNumConfigurations 1
};
static const uint8_t CONFIGURATION_DESCRIPTORS[]
{
0x09, // bLength
0x02, // bDescriptorType (Configuration)
0x2D, 0x00, // wTotalLength 45
0x02, // bNumInterfaces 2
0x01, // bConfigurationValue
0x00, // iConfiguration (String Index)
0x80, // bmAttributes
0x32, // bMaxPower 100mA
0x09, // bLength
0x04, // bDescriptorType (Interface)
0x00, // bInterfaceNumber 0
0x00, // bAlternateSetting
0x01, // bNumEndpoints 1
0x78, // bInterfaceClass
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface (String Index)
0x09, // bLength
0x05, // bDescriptorType (Endpoint)
0x04, // bEndpointAddress (OUT/H2D)
0x05, // bmAttributes (Isochronous, Async, Data EP)
0x30, 0x00, // wMaxPacketSize 48
0x01, // bInterval 1 (unit depends on device speed)
0x00, 0x00,
0x09, // bLength
0x04, // bDescriptorType (Interface)
0x01, // bInterfaceNumber 1
0x00, // bAlternateSetting
0x01, // bNumEndpoints 1
0x78, // bInterfaceClass
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface (String Index)
0x09, // bLength
0x05, // bDescriptorType (Endpoint)
0x85, // bEndpointAddress (IN/D2H)
0x05, // bmAttributes (Isochronous, Async, Data EP)
0x30, 0x00, // wMaxPacketSize 48
0x01, // bInterval 1 (unit depends on device speed)
0x00, 0x00,
};
}; // COM
}; // namespace XboxOG
#endif // _XBOX_OG_DESCRIPTORS_H_

View File

@@ -0,0 +1,63 @@
#ifndef _XBOX_ONE_DESCRIPTORS_H_
#define _XBOX_ONE_DESCRIPTORS_H_
#include <cstdint>
namespace XboxOne
{
namespace Buttons0
{
static constexpr uint8_t SYNC = (1 << 0);
static constexpr uint8_t GUIDE = (1 << 1);
static constexpr uint8_t START = (1 << 2);
static constexpr uint8_t BACK = (1 << 3);
static constexpr uint8_t A = (1 << 4);
static constexpr uint8_t B = (1 << 5);
static constexpr uint8_t X = (1 << 6);
static constexpr uint8_t Y = (1 << 7);
};
namespace Buttons1
{
static constexpr uint8_t DPAD_UP = (1 << 0);
static constexpr uint8_t DPAD_DOWN = (1 << 1);
static constexpr uint8_t DPAD_LEFT = (1 << 2);
static constexpr uint8_t DPAD_RIGHT = (1 << 3);
static constexpr uint8_t LB = (1 << 4);
static constexpr uint8_t RB = (1 << 5);
static constexpr uint8_t L3 = (1 << 6);
static constexpr uint8_t R3 = (1 << 7);
};
#pragma pack(push, 1)
struct InReport
{
struct GipHeader
{
uint8_t command;
uint8_t client : 4;
uint8_t needsAck : 1;
uint8_t internal : 1;
uint8_t chunkStart : 1;
uint8_t chunked : 1;
uint8_t sequence;
uint8_t length;
} header;
uint8_t buttons[2];
uint16_t trigger_l;
uint16_t trigger_r;
int16_t joystick_lx;
int16_t joystick_ly;
int16_t joystick_rx;
int16_t joystick_ry;
uint8_t reserved[18]; // 18-byte padding at the end
};
static_assert(sizeof(InReport) == 36, "XboxOne::InReport is not the correct size");
#pragma pack(pop)
}; // namespace XboxOne
#endif // _XBOX_ONE_DESCRIPTORS_H_

View File

@@ -0,0 +1,640 @@
#ifndef _GAMEPAD_H_
#define _GAMEPAD_H_
#include <cstdint>
#include <atomic>
#include <limits>
#include <cstring>
#include <array>
#include <cmath>
#include <pico/mutex.h>
#include "libfixmath/fix16.hpp"
#include "Gamepad/Range.h"
#include "Gamepad/fix16ext.h"
#include "UserSettings/UserProfile.h"
#include "UserSettings/JoystickSettings.h"
#include "UserSettings/TriggerSettings.h"
#include "Board/ogxm_log.h"
class Gamepad
{
public:
//Defaults used by device to get buttons
static constexpr uint8_t DPAD_UP = 0x01;
static constexpr uint8_t DPAD_DOWN = 0x02;
static constexpr uint8_t DPAD_LEFT = 0x04;
static constexpr uint8_t DPAD_RIGHT = 0x08;
static constexpr uint8_t DPAD_UP_LEFT = DPAD_UP | DPAD_LEFT;
static constexpr uint8_t DPAD_UP_RIGHT = DPAD_UP | DPAD_RIGHT;
static constexpr uint8_t DPAD_DOWN_LEFT = DPAD_DOWN | DPAD_LEFT;
static constexpr uint8_t DPAD_DOWN_RIGHT = DPAD_DOWN | DPAD_RIGHT;
static constexpr uint8_t DPAD_NONE = 0x00;
static constexpr uint16_t BUTTON_A = 0x0001;
static constexpr uint16_t BUTTON_B = 0x0002;
static constexpr uint16_t BUTTON_X = 0x0004;
static constexpr uint16_t BUTTON_Y = 0x0008;
static constexpr uint16_t BUTTON_L3 = 0x0010;
static constexpr uint16_t BUTTON_R3 = 0x0020;
static constexpr uint16_t BUTTON_BACK = 0x0040;
static constexpr uint16_t BUTTON_START = 0x0080;
static constexpr uint16_t BUTTON_LB = 0x0100;
static constexpr uint16_t BUTTON_RB = 0x0200;
static constexpr uint16_t BUTTON_SYS = 0x0400;
static constexpr uint16_t BUTTON_MISC = 0x0800;
static constexpr uint8_t ANALOG_OFF_UP = 0;
static constexpr uint8_t ANALOG_OFF_DOWN = 1;
static constexpr uint8_t ANALOG_OFF_LEFT = 2;
static constexpr uint8_t ANALOG_OFF_RIGHT = 3;
static constexpr uint8_t ANALOG_OFF_A = 4;
static constexpr uint8_t ANALOG_OFF_B = 5;
static constexpr uint8_t ANALOG_OFF_X = 6;
static constexpr uint8_t ANALOG_OFF_Y = 7;
static constexpr uint8_t ANALOG_OFF_LB = 8;
static constexpr uint8_t ANALOG_OFF_RB = 9;
//Mappings used by host to set buttons
uint8_t MAP_DPAD_UP = DPAD_UP ;
uint8_t MAP_DPAD_DOWN = DPAD_DOWN ;
uint8_t MAP_DPAD_LEFT = DPAD_LEFT ;
uint8_t MAP_DPAD_RIGHT = DPAD_RIGHT ;
uint8_t MAP_DPAD_UP_LEFT = DPAD_UP_LEFT ;
uint8_t MAP_DPAD_UP_RIGHT = DPAD_UP_RIGHT ;
uint8_t MAP_DPAD_DOWN_LEFT = DPAD_DOWN_LEFT ;
uint8_t MAP_DPAD_DOWN_RIGHT = DPAD_DOWN_RIGHT;
uint8_t MAP_DPAD_NONE = DPAD_NONE ;
uint16_t MAP_BUTTON_A = BUTTON_A ;
uint16_t MAP_BUTTON_B = BUTTON_B ;
uint16_t MAP_BUTTON_X = BUTTON_X ;
uint16_t MAP_BUTTON_Y = BUTTON_Y ;
uint16_t MAP_BUTTON_L3 = BUTTON_L3 ;
uint16_t MAP_BUTTON_R3 = BUTTON_R3 ;
uint16_t MAP_BUTTON_BACK = BUTTON_BACK ;
uint16_t MAP_BUTTON_START = BUTTON_START;
uint16_t MAP_BUTTON_LB = BUTTON_LB ;
uint16_t MAP_BUTTON_RB = BUTTON_RB ;
uint16_t MAP_BUTTON_SYS = BUTTON_SYS ;
uint16_t MAP_BUTTON_MISC = BUTTON_MISC ;
uint8_t MAP_ANALOG_OFF_UP = ANALOG_OFF_UP ;
uint8_t MAP_ANALOG_OFF_DOWN = ANALOG_OFF_DOWN ;
uint8_t MAP_ANALOG_OFF_LEFT = ANALOG_OFF_LEFT ;
uint8_t MAP_ANALOG_OFF_RIGHT = ANALOG_OFF_RIGHT;
uint8_t MAP_ANALOG_OFF_A = ANALOG_OFF_A ;
uint8_t MAP_ANALOG_OFF_B = ANALOG_OFF_B ;
uint8_t MAP_ANALOG_OFF_X = ANALOG_OFF_X ;
uint8_t MAP_ANALOG_OFF_Y = ANALOG_OFF_Y ;
uint8_t MAP_ANALOG_OFF_LB = ANALOG_OFF_LB ;
uint8_t MAP_ANALOG_OFF_RB = ANALOG_OFF_RB ;
#pragma pack(push, 1)
struct PadIn
{
uint8_t dpad;
uint16_t buttons;
uint8_t trigger_l;
uint8_t trigger_r;
int16_t joystick_lx;
int16_t joystick_ly;
int16_t joystick_rx;
int16_t joystick_ry;
uint8_t analog[10];
PadIn()
{
std::memset(this, 0, sizeof(PadIn));
}
};
struct PadOut
{
uint8_t rumble_l;
uint8_t rumble_r;
PadOut()
{
std::memset(this, 0, sizeof(PadOut));
}
};
using ChatpadIn = std::array<uint8_t, 3>;
#pragma pack(pop)
Gamepad()
{
mutex_init(&pad_in_mutex_);
mutex_init(&pad_out_mutex_);
mutex_init(&chatpad_in_mutex_);
reset_pad_in();
reset_pad_out();
reset_chatpad_in();
};
~Gamepad() = default;
//Get
inline bool new_pad_in() const { return new_pad_in_.load(); }
inline bool new_pad_out() const { return new_pad_out_.load(); }
//True if both host and device have enabled analog
inline bool analog_enabled() const { return analog_enabled_.load(std::memory_order_relaxed); }
inline PadIn get_pad_in()
{
mutex_enter_blocking(&pad_in_mutex_);
PadIn pad_in = pad_in_;
new_pad_in_.store(false);
mutex_exit(&pad_in_mutex_);
return pad_in;
}
inline PadOut get_pad_out()
{
mutex_enter_blocking(&pad_out_mutex_);
PadOut pad_out = pad_out_;
new_pad_out_.store(false);
mutex_exit(&pad_out_mutex_);
return pad_out;
}
inline ChatpadIn get_chatpad_in()
{
mutex_enter_blocking(&chatpad_in_mutex_);
ChatpadIn chatpad_in = chatpad_in_;
mutex_exit(&chatpad_in_mutex_);
return chatpad_in;
}
//Set
void set_analog_device(bool value)
{
analog_device_.store(value);
if (analog_host_.load() && analog_device_.load() && profile_analog_enabled_)
{
analog_enabled_.store(true);
}
}
void set_analog_host(bool value)
{
analog_host_.store(value);
if (analog_host_.load() && analog_device_.load() && profile_analog_enabled_)
{
analog_enabled_.store(true);
}
}
void set_profile(const UserProfile& user_profile)
{
set_profile_mappings(user_profile);
set_profile_settings(user_profile);
}
inline void set_pad_in(PadIn pad_in)
{
mutex_enter_blocking(&pad_in_mutex_);
pad_in_ = pad_in;
new_pad_in_.store(true);
mutex_exit(&pad_in_mutex_);
}
inline void set_pad_out(const PadOut& pad_out)
{
mutex_enter_blocking(&pad_out_mutex_);
pad_out_ = pad_out;
new_pad_out_.store(true);
mutex_exit(&pad_out_mutex_);
}
inline void set_chatpad_in(const ChatpadIn& chatpad_in)
{
mutex_enter_blocking(&chatpad_in_mutex_);
chatpad_in_ = chatpad_in;
mutex_exit(&chatpad_in_mutex_);
}
inline void reset_pad_in()
{
mutex_enter_blocking(&pad_in_mutex_);
pad_in_ = PadIn();
mutex_exit(&pad_in_mutex_);
new_pad_in_.store(true);
}
inline void reset_pad_out()
{
mutex_enter_blocking(&pad_out_mutex_);
pad_out_ = PadOut();
new_pad_out_.store(true);
mutex_exit(&pad_out_mutex_);
}
inline void reset_chatpad_in()
{
mutex_enter_blocking(&chatpad_in_mutex_);
chatpad_in_.fill(0);
mutex_exit(&chatpad_in_mutex_);
}
template <uint8_t bits = 0, typename T>
inline std::pair<int16_t, int16_t> scale_joystick_r(T x, T y, bool invert_y = false) const
{
int16_t joy_x = 0;
int16_t joy_y = 0;
if constexpr (bits > 0)
{
joy_x = Range::scale_from_bits<int16_t, bits>(x);
joy_y = Range::scale_from_bits<int16_t, bits>(y);
}
else if constexpr (!std::is_same_v<T, int16_t>)
{
joy_x = Range::scale<int16_t>(x);
joy_y = Range::scale<int16_t>(y);
}
else
{
joy_x = x;
joy_y = y;
}
return joy_settings_r_en_
? apply_joystick_settings(joy_x, joy_y, joy_settings_r_, invert_y)
: std::make_pair(joy_x, invert_y ? Range::invert(joy_y) : joy_y);
}
template <uint8_t bits = 0, typename T>
inline std::pair<int16_t, int16_t> scale_joystick_l(T x, T y, bool invert_y = false) const
{
int16_t joy_x = 0;
int16_t joy_y = 0;
if constexpr (bits > 0)
{
joy_x = Range::scale_from_bits<int16_t, bits>(x);
joy_y = Range::scale_from_bits<int16_t, bits>(y);
}
else if constexpr (!std::is_same_v<T, int16_t>)
{
joy_x = Range::scale<int16_t>(x);
joy_y = Range::scale<int16_t>(y);
}
else
{
joy_x = x;
joy_y = y;
}
return joy_settings_l_en_
? apply_joystick_settings(joy_x, joy_y, joy_settings_l_, invert_y)
: std::make_pair(joy_x, invert_y ? Range::invert(joy_y) : joy_y);
}
template <uint8_t bits = 0, typename T>
inline uint8_t scale_trigger_l(T value) const
{
uint8_t trigger_value = 0;
if constexpr (bits > 0)
{
trigger_value = Range::scale_from_bits<uint8_t, bits>(value);
}
else if constexpr (!std::is_same_v<T, uint8_t>)
{
trigger_value = Range::scale<uint8_t>(value);
}
else
{
trigger_value = value;
}
return trig_settings_l_en_
? apply_trigger_settings(trigger_value, trig_settings_l_)
: trigger_value;
}
template <uint8_t bits = 0, typename T>
inline uint8_t scale_trigger_r(T value) const
{
uint8_t trigger_value = 0;
if constexpr (bits > 0)
{
trigger_value = Range::scale_from_bits<uint8_t, bits>(value);
}
else if constexpr (!std::is_same_v<T, uint8_t>)
{
trigger_value = Range::scale<uint8_t>(value);
}
else
{
trigger_value = value;
}
return trig_settings_r_en_
? apply_trigger_settings(trigger_value, trig_settings_r_)
: trigger_value;
}
private:
mutex_t pad_in_mutex_;
mutex_t pad_out_mutex_;
mutex_t chatpad_in_mutex_;
PadOut pad_out_;
PadIn pad_in_;
ChatpadIn chatpad_in_{0};
std::atomic<bool> new_pad_in_{false};
std::atomic<bool> new_pad_out_{false};
std::atomic<bool> analog_enabled_{false};
std::atomic<bool> analog_host_{false};
std::atomic<bool> analog_device_{false};
bool profile_analog_enabled_{false};
JoystickSettings joy_settings_l_;
JoystickSettings joy_settings_r_;
TriggerSettings trig_settings_l_;
TriggerSettings trig_settings_r_;
bool joy_settings_l_en_{false};
bool joy_settings_r_en_{false};
bool trig_settings_l_en_{false};
bool trig_settings_r_en_{false};
void set_profile_settings(const UserProfile& profile)
{
profile_analog_enabled_ = profile.analog_enabled ? true : false;
OGXM_LOG("profile_analog_enabled_: %d\n", profile_analog_enabled_);
if ((joy_settings_l_en_ = !joy_settings_l_.is_same(profile.joystick_settings_l)))
{
joy_settings_l_.set_from_raw(profile.joystick_settings_l);
//This needs to be addressed in the webapp, just multiply here for now
joy_settings_l_.axis_restrict *= static_cast<int16_t>(100);
joy_settings_l_.angle_restrict *= static_cast<int16_t>(100);
joy_settings_l_.anti_dz_angular *= static_cast<int16_t>(100);
}
if ((joy_settings_r_en_ = !joy_settings_r_.is_same(profile.joystick_settings_r)))
{
joy_settings_r_.set_from_raw(profile.joystick_settings_r);
//This needs to be addressed in the webapp, just multiply here for now
joy_settings_r_.axis_restrict *= static_cast<int16_t>(100);
joy_settings_r_.angle_restrict *= static_cast<int16_t>(100);
joy_settings_r_.anti_dz_angular *= static_cast<int16_t>(100);
}
if ((trig_settings_l_en_ = !trig_settings_l_.is_same(profile.trigger_settings_l)))
{
trig_settings_l_.set_from_raw(profile.trigger_settings_l);
}
if ((trig_settings_r_en_ = !trig_settings_r_.is_same(profile.trigger_settings_r)))
{
trig_settings_r_.set_from_raw(profile.trigger_settings_r);
}
OGXM_LOG("GamepadMapper: JoyL: %s, JoyR: %s, TrigL: %s, TrigR: %s\n",
joy_settings_l_en_ ? "Enabled" : "Disabled",
joy_settings_r_en_ ? "Enabled" : "Disabled",
trig_settings_l_en_ ? "Enabled" : "Disabled",
trig_settings_r_en_ ? "Enabled" : "Disabled");
}
void set_profile_mappings(const UserProfile& profile)
{
MAP_DPAD_UP = profile.dpad_up;
MAP_DPAD_DOWN = profile.dpad_down;
MAP_DPAD_LEFT = profile.dpad_left;
MAP_DPAD_RIGHT = profile.dpad_right;
MAP_DPAD_UP_LEFT = profile.dpad_up | profile.dpad_left;
MAP_DPAD_UP_RIGHT = profile.dpad_up | profile.dpad_right;
MAP_DPAD_DOWN_LEFT = profile.dpad_down | profile.dpad_left;
MAP_DPAD_DOWN_RIGHT = profile.dpad_down | profile.dpad_right;
MAP_DPAD_NONE = 0;
MAP_BUTTON_A = profile.button_a;
MAP_BUTTON_B = profile.button_b;
MAP_BUTTON_X = profile.button_x;
MAP_BUTTON_Y = profile.button_y;
MAP_BUTTON_L3 = profile.button_l3;
MAP_BUTTON_R3 = profile.button_r3;
MAP_BUTTON_BACK = profile.button_back;
MAP_BUTTON_START = profile.button_start;
MAP_BUTTON_LB = profile.button_lb;
MAP_BUTTON_RB = profile.button_rb;
MAP_BUTTON_SYS = profile.button_sys;
MAP_BUTTON_MISC = profile.button_misc;
MAP_ANALOG_OFF_UP = profile.analog_off_up;
MAP_ANALOG_OFF_DOWN = profile.analog_off_down;
MAP_ANALOG_OFF_LEFT = profile.analog_off_left;
MAP_ANALOG_OFF_RIGHT = profile.analog_off_right;
MAP_ANALOG_OFF_A = profile.analog_off_a;
MAP_ANALOG_OFF_B = profile.analog_off_b;
MAP_ANALOG_OFF_X = profile.analog_off_x;
MAP_ANALOG_OFF_Y = profile.analog_off_y;
MAP_ANALOG_OFF_LB = profile.analog_off_lb;
MAP_ANALOG_OFF_RB = profile.analog_off_rb;
}
static inline std::pair<int16_t, int16_t> apply_joystick_settings(
int16_t gp_joy_x,
int16_t gp_joy_y,
const JoystickSettings& set,
bool invert_y)
{
static const Fix16
FIX_0(0.0f),
FIX_1(1.0f),
FIX_2(2.0f),
FIX_45(45.0f),
FIX_90(90.0f),
FIX_100(100.0f),
FIX_180(180.0f),
FIX_EPSILON(0.0001f),
FIX_EPSILON2(0.001f),
FIX_ELLIPSE_DEF(1.570796f),
FIX_DIAG_DIVISOR(0.29289f);
Fix16 x = (set.invert_x ? Fix16(Range::invert(gp_joy_x)) : Fix16(gp_joy_x)) / Range::MAX<int16_t>;
Fix16 y = ((set.invert_y ^ invert_y) ? Fix16(Range::invert(gp_joy_y)) : Fix16(gp_joy_y)) / Range::MAX<int16_t>;
const Fix16 abs_x = fix16::abs(x);
const Fix16 abs_y = fix16::abs(y);
const Fix16 inv_axis_restrict = FIX_1 / (FIX_1 - set.axis_restrict);
Fix16 rAngle = (abs_x < FIX_EPSILON)
? FIX_90
: fix16::rad2deg(fix16::abs(fix16::atan(y / x)));
Fix16 axial_x = (abs_x <= set.axis_restrict && rAngle > FIX_45)
? FIX_0
: ((abs_x - set.axis_restrict) * inv_axis_restrict);
Fix16 axial_y = (abs_y <= set.axis_restrict && rAngle <= FIX_45)
? FIX_0
: ((abs_y - set.axis_restrict) * inv_axis_restrict);
Fix16 in_magnitude = fix16::sqrt(fix16::sq(axial_x) + fix16::sq(axial_y));
if (in_magnitude < set.dz_inner)
{
return { 0, 0 };
}
Fix16 angle =
fix16::abs(axial_x) < FIX_EPSILON
? FIX_90
: fix16::rad2deg(fix16::abs(fix16::atan(axial_y / axial_x)));
Fix16 anti_r_scale = (set.anti_dz_square_y_scale == FIX_0) ? set.anti_dz_square : set.anti_dz_square_y_scale;
Fix16 anti_dz_c = set.anti_dz_circle;
if (anti_r_scale > FIX_0 && anti_dz_c > FIX_0)
{
Fix16 anti_ellip_scale = anti_ellip_scale / anti_dz_c;
Fix16 ellipse_angle = fix16::atan((FIX_1 / anti_ellip_scale) * fix16::tan(fix16::rad2deg(rAngle)));
ellipse_angle = (ellipse_angle < FIX_0) ? FIX_ELLIPSE_DEF : ellipse_angle;
Fix16 ellipse_x = fix16::cos(ellipse_angle);
Fix16 ellipse_y = fix16::sqrt(fix16::sq(anti_ellip_scale) * (FIX_1 - fix16::sq(ellipse_x)));
anti_dz_c *= fix16::sqrt(fix16::sq(ellipse_x) + fix16::sq(ellipse_y));
}
if (anti_dz_c > FIX_0)
{
anti_dz_c = anti_dz_c / ((anti_dz_c * (FIX_1 - set.anti_dz_circle / set.dz_outer)) / (anti_dz_c * (FIX_1 - set.anti_dz_square)));
}
if (abs_x > set.axis_restrict && abs_y > set.axis_restrict)
{
const Fix16 FIX_ANGLE_MAX = set.angle_restrict / 2.0f;
if (angle > FIX_0 && angle < FIX_ANGLE_MAX)
{
angle = FIX_0;
}
if (angle > (FIX_90 - FIX_ANGLE_MAX))
{
angle = FIX_90;
}
if (angle > FIX_ANGLE_MAX && angle < (FIX_90 - FIX_ANGLE_MAX))
{
angle = ((angle - FIX_ANGLE_MAX) * FIX_90) / ((FIX_90 - FIX_ANGLE_MAX) - FIX_ANGLE_MAX);
}
}
Fix16 ref_angle = (angle < FIX_EPSILON2) ? FIX_0 : angle;
Fix16 diagonal = (angle > FIX_45) ? (((angle - FIX_45) * (-FIX_45)) / FIX_45) + FIX_45 : angle;
const Fix16 angle_comp = set.angle_restrict / FIX_2;
if (angle < FIX_90 && angle > FIX_0)
{
angle = ((angle * ((FIX_90 - angle_comp) - angle_comp)) / FIX_90) + angle_comp;
}
if (axial_x < FIX_0 && axial_y > FIX_0)
{
angle = -angle;
}
if (axial_x > FIX_0 && axial_y < FIX_0)
{
angle = angle - FIX_180;
}
if (axial_x < FIX_0 && axial_y < FIX_0)
{
angle = angle + FIX_180;
}
//Deadzone Warp
Fix16 out_magnitude = (in_magnitude - set.dz_inner) / (set.anti_dz_outer - set.dz_inner);
out_magnitude = fix16::pow(out_magnitude, (FIX_1 / set.curve)) * (set.dz_outer - anti_dz_c) + anti_dz_c;
out_magnitude = (out_magnitude > set.dz_outer && !set.uncap_radius) ? set.dz_outer : out_magnitude;
Fix16 d_scale = (((out_magnitude - anti_dz_c) * (set.diag_scale_max - set.diag_scale_min)) / (set.dz_outer - anti_dz_c)) + set.diag_scale_min;
Fix16 c_scale = (diagonal * (FIX_1 / fix16::sqrt(FIX_2))) / FIX_45; //Both these lines scale the intensity of the warping
c_scale = FIX_1 - fix16::sqrt(FIX_1 - c_scale * c_scale); //based on a circular curve to the perfect diagonal
d_scale = (c_scale * (d_scale - FIX_1)) / FIX_DIAG_DIVISOR + FIX_1;
out_magnitude = out_magnitude * d_scale;
//Scaling values for square antideadzone
Fix16 new_x = fix16::cos(fix16::deg2rad(angle)) * out_magnitude;
Fix16 new_y = fix16::sin(fix16::deg2rad(angle)) * out_magnitude;
//Magic angle wobble fix by user ME.
// if (angle > 45.0 && angle < 225.0) {
// newX = inv(Math.sin(deg2rad(angle - 90.0)))*outputMagnitude;
// newY = inv(Math.cos(deg2rad(angle - 270.0)))*outputMagnitude;
// }
//Square antideadzone scaling
Fix16 output_x = fix16::abs(new_x) * (FIX_1 - set.anti_dz_square / set.dz_outer) + set.anti_dz_square;
if (x < FIX_0)
{
output_x = -output_x;
}
if (ref_angle == FIX_90)
{
output_x = FIX_0;
}
Fix16 output_y = fix16::abs(new_y) * (FIX_1 - anti_r_scale / set.dz_outer) + anti_r_scale;
if (y < FIX_0)
{
output_y = -output_y;
}
if (ref_angle == FIX_0)
{
output_y = FIX_0;
}
output_x = fix16::clamp(output_x, -FIX_1, FIX_1) * Range::MAX<int16_t>;
output_y = fix16::clamp(output_y, -FIX_1, FIX_1) * Range::MAX<int16_t>;
return { static_cast<int16_t>(fix16_to_int(output_x)), static_cast<int16_t>(fix16_to_int(output_y)) };
}
uint8_t apply_trigger_settings(uint8_t value, const TriggerSettings& set) const
{
Fix16 abs_value = fix16::abs(Fix16(static_cast<int16_t>(value)) / static_cast<int16_t>(Range::MAX<uint8_t>));
if (abs_value < set.dz_inner)
{
return 0;
}
static const Fix16
FIX_0(0.0f),
FIX_1(1.0f),
FIX_2(2.0f);
Fix16 value_out = (abs_value - set.dz_inner) / (set.anti_dz_outer - set.dz_inner);
value_out = fix16::clamp(value_out, FIX_0, FIX_1);
if (set.anti_dz_inner > FIX_0)
{
value_out = set.anti_dz_inner + (FIX_1 - set.anti_dz_inner) * value_out;
}
if (set.curve != FIX_1)
{
value_out = fix16::pow(value_out, FIX_1 / set.curve);
}
if (set.anti_dz_outer < FIX_1)
{
value_out = fix16::clamp(value_out * (FIX_1 / (FIX_1 - set.anti_dz_outer)), FIX_0, FIX_1);
}
value_out *= set.dz_outer;
return static_cast<uint8_t>(fix16_to_int(value_out * static_cast<int16_t>(Range::MAX<uint8_t>)));
}
};
#endif // _GAMEPAD_H_

View File

@@ -0,0 +1,204 @@
#ifndef _RANGE_H_
#define _RANGE_H_
#include <cstdint>
#include <limits>
#include <type_traits>
#include "Board/ogxm_log.h"
namespace Range {
template <typename T>
requires std::is_integral_v<T>
constexpr T MAX = std::numeric_limits<T>::max();
template <typename T>
requires std::is_integral_v<T>
constexpr T MIN = std::numeric_limits<T>::min();
template <typename T>
requires std::is_integral_v<T>
constexpr T MID =
[] {
if constexpr (std::is_unsigned_v<T>)
{
return MAX<T> / 2 + 1;
}
return 0;
}();
static_assert(MID<uint8_t> == 128, "MID<uint8_t> != 128");
static_assert(MID<int8_t> == 0, "MID<int8_t> != 0");
template <typename T>
requires std::is_integral_v<T>
constexpr uint16_t NUM_BITS = sizeof(T) * 8;
//Maximum value for a given number of bits, result will be signed/unsigned depending on type
template<typename T, uint8_t bits>
requires std::is_integral_v<T>
constexpr T BITS_MAX()
{
static_assert(bits <= NUM_BITS<T>, "BITS_MAX: Return type cannot represet maximum bit value");
static_assert(bits <= 64, "BITS_MAX: Bits exceed 64");
if constexpr (std::is_unsigned_v<T>)
{
return (1ULL << bits) - 1;
}
return (1LL << (bits - 1)) - 1;
}
static_assert(BITS_MAX<int16_t, 10>() == 511, "BITS_MAX<int16_t>(10) != 511");
static_assert(BITS_MAX<uint16_t, 10>() == 1023, "BITS_MAX<uint16_t>(10) != 1023");
//Minimum value for a given number of bits, result will be signed/unsigned depending on type
template<typename T, uint8_t bits>
requires std::is_integral_v<T>
constexpr T BITS_MIN()
{
static_assert(bits <= NUM_BITS<T>, "BITS_MIN: Return type cannot represet minimum bit value");
static_assert(bits <= 64, "BITS_MIN: Bits exceed 64");
if constexpr (std::is_unsigned_v<T>)
{
return 0;
}
return static_cast<T>(-(1LL << (bits - 1)));
}
static_assert(BITS_MIN<int16_t, 10>() == -512, "BITS_MIN<int16_t>(10) != -512");
static_assert(BITS_MIN<uint16_t, 10>() == 0, "BITS_MIN<uint16_t>(10) != 0");
template <typename T>
static inline T invert(T value)
{
if constexpr (std::is_unsigned_v<T>)
{
return Range::MAX<T> - value;
}
return (value == Range::MIN<T>) ? Range::MAX<T> : -value;
}
template <typename To, typename From>
requires std::is_integral_v<To> && std::is_integral_v<From>
static inline To clamp(From value)
{
return static_cast<To>((value < Range::MIN<To>)
? Range::MIN<To>
: (value > Range::MAX<To>)
? Range::MAX<To>
: value);
}
template <typename T>
static inline T clamp(T value, T min, T max)
{
return (value < min) ? min : (value > max) ? max : value;
}
template <typename To, typename From>
static inline To clamp(From value, To min_to, To max_to)
{
return (value < min_to) ? min_to : (value > max_to) ? max_to : static_cast<To>(value);
}
template <typename To, typename From>
requires std::is_integral_v<To> && std::is_integral_v<From>
static constexpr To scale(From value, From min_from, From max_from, To min_to, To max_to)
{
return static_cast<To>(
(static_cast<int64_t>(value - min_from) * (max_to - min_to) / (max_from - min_from)) + min_to);
}
template <typename To, typename From>
static inline To scale(From value)
{
return scale<To>(value, Range::MIN<From>, Range::MAX<From>, Range::MIN<To>, Range::MAX<To>);
}
template <typename To, typename From>
static inline To scale(From value, To min_to, To max_to)
{
return scale<To>(value, Range::MIN<From>, Range::MAX<From>, min_to, max_to);
}
//Cast value to signed/unsigned for accurate scaling
template <typename To, uint8_t bits, typename From>
static inline To scale_from_bits(From value)
{
return scale<To>(
Range::clamp(value, BITS_MIN<From, bits>(), BITS_MAX<From, bits>()),
BITS_MIN<From, bits>(),
BITS_MAX<From, bits>(),
Range::MIN<To>,
Range::MAX<To>);
}
//Cast value to signed/unsigned for accurate scaling
template <typename To, uint8_t bits, typename From>
static inline To scale_to_bits(From value)
{
return scale<To>(
Range::clamp(value, BITS_MIN<From, bits>(), BITS_MAX<From, bits>()),
Range::MIN<From>,
Range::MAX<From>,
BITS_MIN<To, bits>(),
BITS_MAX<To, bits>());
}
} // namespace Range
namespace Scale //Scale and invert values
{
static inline uint8_t int16_to_uint8(int16_t value)
{
uint16_t shifted_value = static_cast<uint16_t>(value + Range::MID<uint16_t>);
return static_cast<uint8_t>(shifted_value >> 8);
}
static inline uint16_t int16_to_uint16(int16_t value)
{
return static_cast<uint16_t>(value + Range::MID<uint16_t>);
}
static inline int8_t int16_to_int8(int16_t value)
{
return static_cast<int8_t>((value + Range::MID<uint16_t>) >> 8);
}
static inline uint8_t uint16_to_uint8(uint16_t value)
{
return static_cast<uint8_t>(value >> 8);
}
static inline int16_t uint16_to_int16(uint16_t value)
{
return static_cast<int16_t>(value - Range::MID<uint16_t>);
}
static inline int8_t uint16_to_int8(uint16_t value)
{
return static_cast<int8_t>((value >> 8) - Range::MID<uint8_t>);
}
static inline int16_t uint8_to_int16(uint8_t value)
{
return static_cast<int16_t>((static_cast<int32_t>(value) << 8) - Range::MID<uint16_t>);
}
static inline uint16_t uint8_to_uint16(uint8_t value)
{
return static_cast<uint16_t>(value) << 8;
}
static inline int8_t uint8_to_int8(uint8_t value)
{
return static_cast<int8_t>(value - Range::MID<uint8_t>);
}
static inline int16_t int8_to_int16(int8_t value)
{
return static_cast<int16_t>(value) << 8;
}
static inline uint16_t int8_to_uint16(int8_t value)
{
return static_cast<uint16_t>((value + Range::MID<uint8_t>) << 8);
}
static inline uint8_t int8_to_uint8(int8_t value)
{
return static_cast<uint8_t>(value + Range::MID<uint8_t>);
}
} // namespace Scale
#endif // _RANGE_H_

View File

@@ -0,0 +1,106 @@
#ifndef FIX16_EXT_H
#define FIX16_EXT_H
#include <cstdint>
#include "libfixmath/fix16.hpp"
namespace fix16 {
inline Fix16 abs(Fix16 x)
{
return Fix16(fix16_abs(x.value));
}
inline Fix16 rad2deg(Fix16 rad)
{
return Fix16(fix16_rad_to_deg(rad.value));
}
inline Fix16 deg2rad(Fix16 deg)
{
return Fix16(fix16_deg_to_rad(deg.value));
}
inline Fix16 atan(Fix16 x)
{
return Fix16(fix16_atan(x.value));
}
inline Fix16 atan2(Fix16 y, Fix16 x)
{
return Fix16(fix16_atan2(y.value, x.value));
}
inline Fix16 tan(Fix16 x)
{
return Fix16(fix16_tan(x.value));
}
inline Fix16 cos(Fix16 x)
{
return Fix16(fix16_cos(x.value));
}
inline Fix16 sin(Fix16 x)
{
return Fix16(fix16_sin(x.value));
}
inline Fix16 sqrt(Fix16 x)
{
return Fix16(fix16_sqrt(x.value));
}
inline Fix16 sq(Fix16 x)
{
return Fix16(fix16_sq(x.value));
}
inline Fix16 clamp(Fix16 x, Fix16 min, Fix16 max)
{
return Fix16(fix16_clamp(x.value, min.value, max.value));
}
inline Fix16 pow(Fix16 x, Fix16 y)
{
fix16_t& base = x.value;
fix16_t& exponent = y.value;
if (exponent == F16(0.0))
return Fix16(fix16_from_int(1));
if (base == F16(0.0))
return Fix16(fix16_from_int(0));
int32_t int_exp = fix16_to_int(exponent);
if (fix16_from_int(int_exp) == exponent)
{
fix16_t result = F16(1.0);
fix16_t current_base = base;
if (int_exp < 0)
{
current_base = fix16_div(F16(1.0), base);
int_exp = -int_exp;
}
while (int_exp)
{
if (int_exp & 1)
{
result = fix16_mul(result, current_base);
}
current_base = fix16_mul(current_base, current_base);
int_exp >>= 1;
}
return Fix16(result);
}
return Fix16(fix16_exp(fix16_mul(exponent, fix16_log(base))));
}
} // namespace fix16
#endif // FIX16_EXT_H

View File

@@ -0,0 +1,201 @@
#include "Board/Config.h"
#include "OGXMini/Board/ESP32_Bluepad32_I2C.h"
#if (OGXM_BOARD == ESP32_BLUEPAD32_I2C)
#include <cstring>
#include <pico/multicore.h>
#include <pico/i2c_slave.h>
#include <hardware/gpio.h>
#include <hardware/i2c.h>
#include "tusb.h"
#include "bsp/board_api.h"
#include "USBDevice/DeviceManager.h"
#include "UserSettings/UserSettings.h"
#include "Board/board_api.h"
#include "Board/esp32_api.h"
#include "Gamepad/Gamepad.h"
#include "TaskQueue/TaskQueue.h"
enum class PacketID : uint8_t {
UNKNOWN = 0,
SET_PAD,
GET_PAD,
SET_DRIVER
};
#pragma pack(push, 1)
struct PacketIn {
uint8_t packet_len{sizeof(PacketIn)};
PacketID packet_id{PacketID::SET_PAD};
uint8_t index{0};
DeviceDriverType device_type{DeviceDriverType::NONE};
Gamepad::PadIn pad_in{Gamepad::PadIn()};
uint8_t reserved[5]{0};
};
static_assert(sizeof(PacketIn) == 32, "i2c_driver_esp::PacketIn size mismatch");
struct PacketOut {
uint8_t packet_len{sizeof(PacketOut)};
PacketID packet_id{PacketID::GET_PAD};
uint8_t index{0};
Gamepad::PadOut pad_out{Gamepad::PadOut()};
uint8_t reserved[3]{0};
};
static_assert(sizeof(PacketOut) == 8, "i2c_driver_esp::PacketOut size mismatch");
#pragma pack(pop)
constexpr size_t MAX_BUFFER_SIZE = std::max(sizeof(PacketOut), sizeof(PacketIn));
constexpr uint8_t I2C_ADDR = 0x01;
static Gamepad _gamepads[MAX_GAMEPADS];
static bool _uart_bridge_mode = false;
static inline void slave_handler(i2c_inst_t *i2c, i2c_slave_event_t event) {
static size_t count = 0;
static PacketIn packet_in;
static PacketOut packet_out;
static DeviceDriverType current_device_type =
UserSettings::get_instance().get_current_driver();
switch (event) {
case I2C_SLAVE_RECEIVE:
if (count < sizeof(PacketIn)) {
reinterpret_cast<uint8_t*>(&packet_in)[count] = i2c_read_byte_raw(i2c);
++count;
}
break;
case I2C_SLAVE_FINISH:
if (count == 0) {
break;
}
switch (packet_in.packet_id) {
case PacketID::SET_PAD:
if (packet_in.index < MAX_GAMEPADS) {
_gamepads[packet_in.index].set_pad_in(packet_in.pad_in);
}
break;
case PacketID::SET_DRIVER:
if (packet_in.device_type != DeviceDriverType::NONE &&
packet_in.device_type != current_device_type) {
OGXM_LOG("I2C: Driver change detected.\n");
//Any writes to flash should be done on Core0
TaskQueue::Core0::queue_delayed_task(
TaskQueue::Core0::get_new_task_id(), 1000, false,
[new_device_type = packet_in.device_type] {
UserSettings::get_instance().store_driver_type(new_device_type);
}
);
}
break;
default:
break;
}
count = 0;
break;
case I2C_SLAVE_REQUEST:
if (packet_in.index < MAX_GAMEPADS) {
packet_out.index = packet_in.index;
packet_out.pad_out = _gamepads[packet_in.index].get_pad_out();
}
i2c_write_raw_blocking( i2c,
reinterpret_cast<const uint8_t*>(&packet_out),
packet_out.packet_len);
break;
default:
break;
}
}
static void core1_task() {
i2c_init(I2C_PORT, I2C_BAUDRATE);
gpio_init(I2C_SDA_PIN);
gpio_init(I2C_SCL_PIN);
gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C);
gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C);
gpio_pull_up(I2C_SDA_PIN);
gpio_pull_up(I2C_SCL_PIN);
i2c_slave_init(I2C_PORT, I2C_ADDR, &slave_handler);
OGXM_LOG("I2C Driver initialized\n");
while (true) {
tight_loop_contents();
}
}
void run_uart_bridge() {
esp32_api::enter_programming_mode();
OGXM_LOG("Entering UART Bridge mode\n");
//Runs UART Bridge task, doesn't return unless programming is complete
DeviceManager::get_instance().get_driver()->process(0, _gamepads[0]);
OGXM_LOG("Exiting UART Bridge mode\n");
board_api::usb::disconnect_all();
UserSettings::get_instance().write_datetime();
board_api::reboot();
}
bool update_needed(UserSettings& user_settings) {
#if defined(OGXM_RETAIL)
return !user_settings.verify_datetime();
#endif
return false;
}
void esp32_bp32_i2c::initialize() {
board_api::init_board();
esp32_api::init();
UserSettings& user_settings = UserSettings::get_instance();
user_settings.initialize_flash();
//MODE_SEL_PIN is used to determine if UART bridge should be run
_uart_bridge_mode =
(esp32_api::uart_bridge_mode() || update_needed(user_settings));
DeviceDriverType driver_type =
_uart_bridge_mode ?
DeviceDriverType::UART_BRIDGE : user_settings.get_current_driver();
DeviceManager::get_instance().initialize_driver(driver_type, _gamepads);
}
void esp32_bp32_i2c::run() {
if (_uart_bridge_mode) {
run_uart_bridge();
return;
}
multicore_reset_core1();
multicore_launch_core1(core1_task);
esp32_api::reset();
DeviceDriver* device_driver = DeviceManager::get_instance().get_driver();
tud_init(BOARD_TUD_RHPORT);
while (true) {
TaskQueue::Core0::process_tasks();
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i) {
device_driver->process(i, _gamepads[i]);
tud_task();
}
sleep_ms(1);
}
}
// #else // OGXM_BOARD == ESP32_BLUEPAD32_I2C
// void esp32_bp32_i2c::initialize() {}
// void esp32_bp32_i2c::run() {}
#endif // OGXM_BOARD == ESP32_BLUEPAD32_I2C

View File

@@ -0,0 +1,8 @@
#pragma once
#include <cstdint>
namespace esp32_bp32_i2c {
void initialize();
void run();
} // namespace esp32_bp32_i2c

View File

@@ -0,0 +1,200 @@
#include "Board/Config.h"
#include "OGXMini/Board/ESP32_Blueretro_I2C.h"
#if (OGXM_BOARD == ESP32_BLUERETRO_I2C)
#include <pico/multicore.h>
#include <hardware/gpio.h>
#include <hardware/i2c.h>
#include "tusb.h"
#include "bsp/board_api.h"
#include "UserSettings/UserSettings.h"
#include "USBDevice/DeviceManager.h"
#include "Board/board_api.h"
#include "Board/esp32_api.h"
#include "Gamepad/Gamepad.h"
#include "TaskQueue/TaskQueue.h"
#pragma pack(push, 1)
struct PacketIn {
uint8_t len{sizeof(PacketIn)};
uint8_t index{0};
DeviceDriverType device_type{DeviceDriverType::NONE};
uint8_t gp_data[13]{0};
};
static_assert(sizeof(PacketIn) == 16, "i2c_driver_esp::PacketIn size mismatch");
struct PacketOut {
uint8_t len{sizeof(PacketOut)};
uint8_t rumble_l{0};
uint8_t rumble_r{0};
uint8_t reserved[5]{0};
};
static_assert(sizeof(PacketOut) == 8, "i2c_driver_esp::PacketOut size mismatch");
#pragma pack(pop)
constexpr uint8_t SLAVE_ADDR = 0x50;
constexpr uint32_t FEEDBACK_DELAY_MS = 250;
static Gamepad _gamepads[MAX_GAMEPADS];
static bool _uart_bridge_mode = false;
static void core1_task() {
i2c_init(I2C_PORT, I2C_BAUDRATE);
gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C);
gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C);
gpio_pull_up(I2C_SCL_PIN);
gpio_pull_up(I2C_SDA_PIN);
bool slave_ready = false;
PacketIn packet_in;
PacketOut packet_out;
Gamepad::PadIn pad_in;
Gamepad& gamepad = _gamepads[0];
uint32_t tid = TaskQueue::Core1::get_new_task_id();
sleep_ms(500); // Wait for ESP32 to start
TaskQueue::Core1::queue_delayed_task(tid, FEEDBACK_DELAY_MS, true,
[&packet_out, &gamepad, &slave_ready] {
if (!slave_ready) { // Check if slave present
uint8_t addr = SLAVE_ADDR;
int result = i2c_read_blocking(I2C_PORT, SLAVE_ADDR, &addr, 1, false);
slave_ready = (result == 1);
} else { // Update rumble
Gamepad::PadOut pad_out = gamepad.get_pad_out();
packet_out.rumble_l = pad_out.rumble_l;
packet_out.rumble_r = pad_out.rumble_r;
int result = i2c_write_blocking(I2C_PORT, SLAVE_ADDR,
reinterpret_cast<const uint8_t*>(&packet_out),
sizeof(PacketOut), false);
if (result != sizeof(PacketOut)) {
OGXM_LOG("I2C write failed\n");
} else {
OGXM_LOG("I2C sent rumble, L: %02X, R: %02X\n",
packet_out.rumble_l, packet_out.rumble_r);
sleep_ms(1);
}
}
});
OGXM_LOG("I2C Driver initialized\n");
//Wait for slave to be detected
while (!slave_ready) {
TaskQueue::Core1::process_tasks();
sleep_ms(100);
}
OGXM_LOG("I2C Slave ready\n");
while (true) {
TaskQueue::Core1::process_tasks();
int result = i2c_read_blocking( I2C_PORT, SLAVE_ADDR,
reinterpret_cast<uint8_t*>(&packet_in),
sizeof(PacketIn), false);
if (result == sizeof(PacketIn)) {
std::memcpy(reinterpret_cast<uint8_t*>(&pad_in),
packet_in.gp_data,
sizeof(packet_in.gp_data));
gamepad.set_pad_in(pad_in);
} else {
OGXM_LOG("I2C read failed\n");
return;
}
sleep_ms(1);
}
}
void set_gp_check_timer(uint32_t task_id) {
UserSettings& user_settings = UserSettings::get_instance();
TaskQueue::Core0::queue_delayed_task(task_id, UserSettings::GP_CHECK_DELAY_MS, true,
[&user_settings] {
//Check gamepad inputs for button combo to change usb device driver
if (user_settings.check_for_driver_change(_gamepads[0])) {
OGXM_LOG("Driver change detected, storing new driver.\n");
//This will store the new mode and reboot the pico
user_settings.store_driver_type(user_settings.get_current_driver());
}
});
}
void run_uart_bridge() {
esp32_api::enter_programming_mode();
OGXM_LOG("Entering UART Bridge mode\n");
//Runs UART Bridge task, doesn't return unless programming is complete
DeviceManager::get_instance().get_driver()->process(0, _gamepads[0]);
OGXM_LOG("Exiting UART Bridge mode\n");
board_api::usb::disconnect_all();
UserSettings::get_instance().write_datetime();
board_api::reboot();
}
bool update_needed(UserSettings& user_settings) {
#if defined(OGXM_RETAIL)
return !user_settings.verify_datetime();
#endif
return false;
}
void esp32_br_i2c::initialize() {
board_api::init_board();
esp32_api::init();
UserSettings& user_settings = UserSettings::get_instance();
user_settings.initialize_flash();
//MODE_SEL_PIN is used to determine if UART bridge should be run
_uart_bridge_mode =
(esp32_api::uart_bridge_mode() || update_needed(user_settings));
DeviceDriverType driver_type =
_uart_bridge_mode ?
DeviceDriverType::UART_BRIDGE : user_settings.get_current_driver();
DeviceManager::get_instance().initialize_driver(driver_type, _gamepads);
}
void esp32_br_i2c::run() {
if (_uart_bridge_mode) {
run_uart_bridge();
return;
}
esp32_api::reset();
multicore_reset_core1();
multicore_launch_core1(core1_task);
uint32_t tid_gp_check = TaskQueue::Core0::get_new_task_id();
set_gp_check_timer(tid_gp_check);
DeviceDriver* device_driver = DeviceManager::get_instance().get_driver();
tud_init(BOARD_TUD_RHPORT);
while (true) {
TaskQueue::Core0::process_tasks();
device_driver->process(0, _gamepads[0]);
tud_task();
sleep_ms(1);
}
}
// #else // OGXM_BOARD == ESP32_BLUERETRO_I2C
// void esp32_br_i2c::initialize() {}
// void esp32_br_i2c::run() {}
#endif // OGXM_BOARD == ESP32_BLUERETRO_I2C

View File

@@ -0,0 +1,8 @@
#pragma once
#include <cstdint>
namespace esp32_br_i2c {
void initialize();
void run();
} // namespace esp32_br_i2c

View File

@@ -0,0 +1,470 @@
#include "Board/Config.h"
#include "OGXMini/Board/Four_Channel_I2C.h"
#if ((OGXM_BOARD == INTERNAL_4CH_I2C) || (OGXM_BOARD == EXTERNAL_4CH_I2C))
#include <atomic>
#include <cstring>
#include <pico/multicore.h>
#include <hardware/gpio.h>
#include <hardware/i2c.h>
#include <pico/i2c_slave.h>
#include "tusb.h"
#include "bsp/board_api.h"
#include "pio_usb.h"
#include "USBDevice/DeviceManager.h"
#include "USBHost/HostManager.h"
#include "Board/board_api.h"
#include "Board/ogxm_log.h"
#include "UserSettings/UserSettings.h"
#include "Gamepad/Gamepad.h"
#include "TaskQueue/TaskQueue.h"
constexpr uint32_t FEEDBACK_DELAY_MS = 250;
Gamepad _gamepads[MAX_GAMEPADS];
namespace I2C {
enum class Role {
SLAVE = 0,
MASTER
};
enum class PacketID : uint8_t {
UNKNOWN = 0,
PAD,
COMMAND
};
enum class Command : uint8_t {
UNKNOWN = 0,
STATUS,
DISABLE
};
enum class Status : uint8_t {
UNKNOWN = 0,
NC,
ERROR,
OK,
READY,
NOT_READY
};
#pragma pack(push, 1)
struct PacketIn {
uint8_t packet_len{sizeof(PacketIn)};
PacketID packet_id{PacketID::PAD};
Gamepad::PadIn pad_in{Gamepad::PadIn()};
Gamepad::ChatpadIn chatpad_in{0};
uint8_t reserved[4]{0};
};
static_assert(sizeof(PacketIn) == 32, "I2CDriver::PacketIn is misaligned");
struct PacketOut {
uint8_t packet_len{sizeof(PacketOut)};
PacketID packet_id{PacketID::PAD};
Gamepad::PadOut pad_out{Gamepad::PadOut()};
uint8_t reserved[4]{0};
};
static_assert(sizeof(PacketOut) == 8, "I2CDriver::PacketOut is misaligned");
struct PacketCMD {
uint8_t packet_len{sizeof(PacketCMD)};
PacketID packet_id{PacketID::COMMAND};
Command command{Command::UNKNOWN};
Status status{Status::UNKNOWN};
uint8_t reserved[4]{0};
};
static_assert(sizeof(PacketCMD) == 8, "I2CDriver::PacketCMD is misaligned");
#pragma pack(pop)
constexpr size_t MAX_PACKET_SIZE = sizeof(PacketIn);
static Role _i2c_role = Role::SLAVE;
namespace Slave {
static inline PacketID get_packet_id(uint8_t* buffer_in) {
switch (static_cast<PacketID>(buffer_in[1])) {
case PacketID::PAD:
if (buffer_in[0] == sizeof(PacketIn)) {
return PacketID::PAD;
}
break;
case PacketID::COMMAND:
if (buffer_in[0] == sizeof(PacketCMD)) {
return PacketID::COMMAND;
}
break;
default:
break;
}
return PacketID::UNKNOWN;
}
static void slave_handler(i2c_inst_t *i2c, i2c_slave_event_t event) {
static size_t count = 0;
static bool enabled = false;
static uint8_t buffer_in[MAX_PACKET_SIZE];
static uint8_t buffer_out[MAX_PACKET_SIZE];
PacketIn *packet_in_p = reinterpret_cast<PacketIn*>(buffer_in);
PacketOut *packet_out_p = reinterpret_cast<PacketOut*>(buffer_out);
PacketCMD *packet_cmd_in_p = reinterpret_cast<PacketCMD*>(buffer_in);
PacketCMD *packet_cmd_out_p = reinterpret_cast<PacketCMD*>(buffer_out);
switch (event) {
case I2C_SLAVE_RECEIVE: // master has written
if (count < MAX_PACKET_SIZE) {
buffer_in[count] = i2c_read_byte_raw(i2c);
++count;
}
break;
case I2C_SLAVE_FINISH:
// Each master write has an ID indicating the type of data to send back on the next read request
// Every write has an associated read
switch (get_packet_id(buffer_in)) {
case PacketID::PAD:
// instance_->packet_in_ = *packet_in_p;
// *packet_out_p = instance_->packet_out_;
// instance_->new_pad_in_.store(true);
_gamepads[0].set_pad_in(packet_in_p->pad_in);
if (_gamepads[0].new_pad_out()) {
packet_out_p->pad_out = _gamepads[0].get_pad_out();
}
break;
case PacketID::COMMAND:
switch (packet_cmd_in_p->command) {
case Command::DISABLE:
packet_cmd_out_p->packet_len = sizeof(PacketCMD);
packet_cmd_out_p->packet_id = PacketID::COMMAND;
packet_cmd_out_p->command = Command::DISABLE;
packet_cmd_out_p->status = Status::OK;
if (!tuh_mounted(BOARD_TUH_RHPORT)) {
four_ch_i2c::host_mounted(false);
}
break;
case Command::STATUS:
packet_cmd_out_p->packet_len = sizeof(PacketCMD);
packet_cmd_out_p->packet_id = PacketID::COMMAND;
packet_cmd_out_p->command = Command::STATUS;
packet_cmd_out_p->status =
tuh_mounted(BOARD_TUH_RHPORT) ? Status::NOT_READY : Status::READY;
if (!tuh_mounted(BOARD_TUH_RHPORT) && !enabled) {
enabled = true;
four_ch_i2c::host_mounted(true);
}
break;
default:
break;
}
break;
}
count = 0;
std::memset(buffer_in, 0, sizeof(buffer_in));
break;
case I2C_SLAVE_REQUEST:
i2c_write_raw_blocking(i2c, buffer_out, buffer_out[0]);
break;
default:
break;
}
}
} // namespace Slave
namespace Master {
struct Slave {
uint8_t address{0xFF};
Status status{Status::NC};
bool enabled{false};
};
static constexpr size_t NUM_SLAVES = MAX_GAMEPADS - 1;
static_assert(NUM_SLAVES > 0, "I2CMaster::NUM_SLAVES must be greater than 0 to use I2C");
std::array<Slave, NUM_SLAVES> _slaves;
static inline bool read_blocking(uint8_t address, void* buffer, size_t len) {
return (i2c_read_blocking( I2C_PORT, address, reinterpret_cast<uint8_t*>(buffer),
len, false) == static_cast<int>(len));
}
static inline bool write_blocking(uint8_t address, void* buffer, size_t len) {
return (i2c_write_blocking( I2C_PORT, address, reinterpret_cast<uint8_t*>(buffer),
len, false) == static_cast<int>(len));
}
static inline bool slave_detected(uint8_t address) {
uint8_t dummy = 0;
int result = i2c_write_timeout_us(I2C_PORT, address, &dummy, 0, false, 1000);
return (result >= 0);
}
static void notify_disable(uint8_t address) {
if (!slave_detected(address)) {
return;
}
int retries = 10;
while (retries--) {
PacketCMD packet_cmd;
packet_cmd.packet_id = PacketID::COMMAND;
packet_cmd.command = Command::DISABLE;
if (write_blocking(address, &packet_cmd, sizeof(PacketCMD))) {
if (read_blocking(address, &packet_cmd, sizeof(PacketCMD))) {
if (packet_cmd.status == Status::OK) {
break;
}
}
}
sleep_ms(1);
}
}
static void process() {
for (uint8_t i = 0; i < NUM_SLAVES; ++i) {
Slave& slave = _slaves[i];
if (!slave.enabled || !slave_detected(slave.address)) {
continue;
}
PacketCMD packet_cmd;
packet_cmd.packet_id = PacketID::COMMAND;
packet_cmd.command = Command::STATUS;
if (!write_blocking(slave.address, &packet_cmd, sizeof(PacketCMD)) ||
!read_blocking(slave.address, &packet_cmd, sizeof(PacketCMD))) {
continue;
}
if (packet_cmd.status == Status::READY) {
Gamepad& gamepad = _gamepads[i + 1];
PacketIn packet_in;
packet_in.pad_in = gamepad.get_pad_in();
packet_in.chatpad_in = gamepad.get_chatpad_in();
if (write_blocking(slave.address, &packet_in, sizeof(PacketIn))) {
PacketOut packet_out;
if (read_blocking(slave.address, &packet_out, sizeof(PacketOut))) {
gamepad.set_pad_out(packet_out.pad_out);
}
}
}
sleep_ms(1);
}
}
static void xbox360w_connect(bool connected, uint8_t idx) {
if (idx < 1 || idx >= MAX_GAMEPADS) {
return;
}
// This function can be called from core1
// so queue on core0 (i2c thread)
TaskQueue::Core0::queue_task(
[&slave = _slaves[idx - 1], connected]() {
slave.enabled = connected;
if (!connected) {
notify_disable(slave.address);
}
});
}
static void tuh_connect(bool connected, HostDriverType host_type) {
if (host_type != HostDriverType::XBOX360W) {
return;
}
if (!connected) {
//Called from core1 so queue on core0
TaskQueue::Core0::queue_task(
[]() {
for (auto& slave : _slaves) {
slave.enabled = false;
notify_disable(slave.address);
}
});
}
}
} // namespace Master
Role role() {
return _i2c_role;
}
uint8_t get_address() {
gpio_init(SLAVE_ADDR_PIN_1);
gpio_init(SLAVE_ADDR_PIN_2);
gpio_pull_up(SLAVE_ADDR_PIN_1);
gpio_pull_up(SLAVE_ADDR_PIN_2);
if (gpio_get(SLAVE_ADDR_PIN_1) && gpio_get(SLAVE_ADDR_PIN_2)) {
return 0x00;
}
else if (gpio_get(SLAVE_ADDR_PIN_1) && !gpio_get(SLAVE_ADDR_PIN_2)) {
return 0x01;
}
else if (!gpio_get(SLAVE_ADDR_PIN_1) && gpio_get(SLAVE_ADDR_PIN_2)) {
return 0x02;
}
else if (!gpio_get(SLAVE_ADDR_PIN_1) && !gpio_get(SLAVE_ADDR_PIN_2)) {
return 0x03;
}
return 0xFF;
}
void initialize() {
uint8_t i2c_address = get_address();
_i2c_role = (i2c_address == 0xFF) ? Role::MASTER : _i2c_role = Role::SLAVE;
i2c_init(I2C_PORT, I2C_BAUDRATE);
gpio_init(I2C_SDA_PIN);
gpio_init(I2C_SCL_PIN);
gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C);
gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C);
gpio_pull_up(I2C_SDA_PIN);
gpio_pull_up(I2C_SCL_PIN);
if (_i2c_role == Role::SLAVE) {
i2c_slave_init(I2C_PORT, i2c_address, &Slave::slave_handler);
}
}
} // namespace I2C
void core1_task() {
HostManager& host_manager = HostManager::get_instance();
host_manager.initialize(_gamepads);
//Pico-PIO-USB will not reliably detect a hot plug on some boards,
//so monitor pins and init host stack after connection
while(!board_api::usb::host_connected()) {
sleep_ms(100);
}
pio_usb_configuration_t pio_cfg = PIO_USB_CONFIG;
tuh_configure(BOARD_TUH_RHPORT, TUH_CFGID_RPI_PIO_USB_CONFIGURATION, &pio_cfg);
tuh_init(BOARD_TUH_RHPORT);
uint32_t tid_feedback = TaskQueue::Core1::get_new_task_id();
TaskQueue::Core1::queue_delayed_task(tid_feedback, FEEDBACK_DELAY_MS, true,
[&host_manager] {
host_manager.send_feedback();
});
while (true) {
TaskQueue::Core1::process_tasks();
tuh_task();
}
}
void set_gp_check_timer(uint32_t task_id) {
UserSettings& user_settings = UserSettings::get_instance();
TaskQueue::Core0::queue_delayed_task(task_id, UserSettings::GP_CHECK_DELAY_MS, true,
[&user_settings] {
//Check gamepad inputs for button combo to change usb device driver
if (user_settings.check_for_driver_change(_gamepads[0]))
{
//This will store the new mode and reboot the pico
user_settings.store_driver_type(user_settings.get_current_driver());
}
});
}
void four_ch_i2c::wireless_connected(bool connected, uint8_t idx) {
if (I2C::role() == I2C::Role::MASTER) {
I2C::Master::xbox360w_connect(connected, idx);
}
}
void four_ch_i2c::host_mounted(bool mounted) {
static std::atomic<bool> tud_is_inited = false;
board_api::set_led(mounted);
if (!mounted && tud_is_inited.load()) {
TaskQueue::Core0::queue_task([]() {
OGXM_LOG("Disconnecting USB and rebooting.\n");
board_api::usb::disconnect_all();
board_api::reboot();
});
} else if (!tud_is_inited.load() && mounted) {
TaskQueue::Core0::queue_task([]() {
OGXM_LOG("Initializing USB device stack.\n");
tud_init(BOARD_TUD_RHPORT);
tud_is_inited.store(true);
});
}
}
void four_ch_i2c::host_mounted_w_type(bool mounted, HostDriverType host_type) {
host_mounted(mounted);
if (I2C::role() == I2C::Role::MASTER) {
I2C::Master::tuh_connect(mounted, host_type);
}
}
void four_ch_i2c::initialize() {
UserSettings& user_settings = UserSettings::get_instance();
user_settings.initialize_flash();
board_api::init_board();
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i) {
_gamepads[i].set_profile(user_settings.get_profile_by_index(i));
}
DeviceManager::get_instance().initialize_driver(user_settings.get_current_driver(), _gamepads);
}
void four_ch_i2c::run() {
I2C::initialize();
multicore_reset_core1();
multicore_launch_core1(core1_task);
//Wait for something to call tud_init
while (!tud_inited()) {
TaskQueue::Core0::process_tasks();
sleep_ms(100);
}
uint32_t tid_gp_check = TaskQueue::Core0::get_new_task_id();
set_gp_check_timer(tid_gp_check);
DeviceDriver* device_driver = DeviceManager::get_instance().get_driver();
if (I2C::role() == I2C::Role::MASTER) {
while (true) {
TaskQueue::Core0::process_tasks();
I2C::Master::process();
device_driver->process(0, _gamepads[0]);
tud_task();
sleep_ms(1);
}
} else {
while (true) {
TaskQueue::Core0::process_tasks();
device_driver->process(0, _gamepads[0]);
tud_task();
sleep_ms(1);
}
}
}
// #else // OGXM_BOARD == INTERNAL_4CH_I2C || OGXM_BOARD == EXTERNAL_4CH_I2C
// void four_ch_i2c::initialize() {}
// void four_ch_i2c::run() {}
// void four_ch_i2c::host_mounted_w_type(bool mounted, HostDriverType host_type) {}
// void four_ch_i2c::host_mounted(bool mounted) {}
// void four_ch_i2c::wireless_connected(bool connected, uint8_t idx) {}
#endif // OGXM_BOARD == INTERNAL_4CH || OGXM_BOARD == EXTERNAL_4CH

View File

@@ -0,0 +1,13 @@
#pragma once
#include <cstdint>
#include "USBHost/HostDriver/HostDriverTypes.h"
namespace four_ch_i2c {
void initialize();
void run();
void host_mounted_w_type(bool mounted, HostDriverType host_type);
void host_mounted(bool mounted);
void wireless_connected(bool connected, uint8_t idx);
} // namespace ext_4ch_i2c

View File

@@ -0,0 +1,81 @@
#include "Board/Config.h"
#include "OGXMini/Board/PicoW.h"
#if (OGXM_BOARD == PI_PICOW)
#include <hardware/clocks.h>
#include <pico/multicore.h>
#include "tusb.h"
#include "bsp/board_api.h"
#include "USBDevice/DeviceManager.h"
#include "UserSettings/UserSettings.h"
#include "Board/board_api.h"
#include "Bluepad32/Bluepad32.h"
#include "BLEServer/BLEServer.h"
#include "Gamepad/Gamepad.h"
#include "TaskQueue/TaskQueue.h"
Gamepad _gamepads[MAX_GAMEPADS];
void core1_task() {
board_api::init_bluetooth();
board_api::set_led(true);
BLEServer::init_server(_gamepads);
bluepad32::run_task(_gamepads);
}
void set_gp_check_timer(uint32_t task_id) {
UserSettings& user_settings = UserSettings::get_instance();
TaskQueue::Core0::queue_delayed_task(task_id, UserSettings::GP_CHECK_DELAY_MS, true,
[&user_settings] {
//Check gamepad inputs for button combo to change usb device driver
if (user_settings.check_for_driver_change(_gamepads[0])) {
//This will store the new mode and reboot the pico
user_settings.store_driver_type(user_settings.get_current_driver());
}
});
}
void pico_w::initialize() {
board_api::init_board();
UserSettings& user_settings = UserSettings::get_instance();
user_settings.initialize_flash();
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i) {
_gamepads[i].set_profile(user_settings.get_profile_by_index(i));
}
DeviceManager& device_manager = DeviceManager::get_instance();
device_manager.initialize_driver(user_settings.get_current_driver(), _gamepads);
}
void pico_w::run() {
multicore_reset_core1();
multicore_launch_core1(core1_task);
uint32_t tid_gp_check = TaskQueue::Core0::get_new_task_id();
set_gp_check_timer(tid_gp_check);
DeviceDriver* device_driver = DeviceManager::get_instance().get_driver();
tud_init(BOARD_TUD_RHPORT);
while (true) {
TaskQueue::Core0::process_tasks();
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i) {
device_driver->process(i, _gamepads[i]);
tud_task();
}
sleep_ms(1);
}
}
// #else // (OGXM_BOARD == PI_PICOW)
// void pico_w::initialize() {}
// void pico_w::run() {}
#endif // (OGXM_BOARD == PI_PICOW)

View File

@@ -0,0 +1,8 @@
#pragma once
#include <cstdint>
namespace pico_w {
void initialize();
void run();
} // namespace pico_w

View File

@@ -0,0 +1,134 @@
#include "Board/Config.h"
#include "OGXMini/Board/Standard.h"
#if ((OGXM_BOARD == PI_PICO) || (OGXM_BOARD == RP2040_ZERO) || (OGXM_BOARD == ADAFRUIT_FEATHER))
#include <pico/multicore.h>
#include "tusb.h"
#include "bsp/board_api.h"
#include "pio_usb.h"
#include "USBHost/HostManager.h"
#include "USBDevice/DeviceManager.h"
#include "TaskQueue/TaskQueue.h"
#include "Gamepad/Gamepad.h"
#include "Board/board_api.h"
#include "Board/ogxm_log.h"
constexpr uint32_t FEEDBACK_DELAY_MS = 200;
Gamepad _gamepads[MAX_GAMEPADS];
void core1_task() {
HostManager& host_manager = HostManager::get_instance();
host_manager.initialize(_gamepads);
//Pico-PIO-USB will not reliably detect a hot plug on some boards,
//monitor and init host stack after connection
while(!board_api::usb::host_connected()) {
sleep_ms(100);
}
pio_usb_configuration_t pio_cfg = PIO_USB_CONFIG;
tuh_configure(BOARD_TUH_RHPORT, TUH_CFGID_RPI_PIO_USB_CONFIGURATION, &pio_cfg);
tuh_init(BOARD_TUH_RHPORT);
uint32_t tid_feedback = TaskQueue::Core1::get_new_task_id();
TaskQueue::Core1::queue_delayed_task(tid_feedback, FEEDBACK_DELAY_MS, true,
[&host_manager] {
host_manager.send_feedback();
});
while (true) {
TaskQueue::Core1::process_tasks();
tuh_task();
}
}
void set_gp_check_timer(uint32_t task_id) {
UserSettings& user_settings = UserSettings::get_instance();
TaskQueue::Core0::queue_delayed_task(task_id, UserSettings::GP_CHECK_DELAY_MS, true,
[&user_settings] {
//Check gamepad inputs for button combo to change usb device driver
if (user_settings.check_for_driver_change(_gamepads[0])) {
OGXM_LOG("Driver change detected, storing new driver.\n");
//This will store the new mode and reboot the pico
user_settings.store_driver_type(user_settings.get_current_driver());
}
});
}
//Called by tusb host so we know to connect or disconnect usb
void standard::host_mounted(bool host_mounted) {
static std::atomic<bool> tud_is_inited = false;
board_api::set_led(host_mounted);
if (!host_mounted && tud_is_inited.load()) {
TaskQueue::Core0::queue_task([]() {
OGXM_LOG("USB disconnected, rebooting.\n");
board_api::usb::disconnect_all();
board_api::reboot();
});
} else if (!tud_is_inited.load()) {
TaskQueue::Core0::queue_task([]() {
tud_init(BOARD_TUD_RHPORT);
tud_is_inited.store(true);
});
}
}
void standard::initialize() {
board_api::init_board();
UserSettings& user_settings = UserSettings::get_instance();
user_settings.initialize_flash();
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i) {
_gamepads[i].set_profile(user_settings.get_profile_by_index(i));
}
DeviceManager::get_instance().initialize_driver(user_settings.get_current_driver(), _gamepads);
}
void standard::run() {
multicore_reset_core1();
multicore_launch_core1(core1_task);
DeviceDriverType current_driver = UserSettings::get_instance().get_current_driver();
if (current_driver != DeviceDriverType::WEBAPP) {
// Wait for something to call host_mounted()
while (!tud_inited()) {
TaskQueue::Core0::process_tasks();
sleep_ms(100);
}
} else {
//Connect immediately in WebApp mode
host_mounted(true);
}
uint32_t tid_gp_check = TaskQueue::Core0::get_new_task_id();
set_gp_check_timer(tid_gp_check);
DeviceDriver* device_driver = DeviceManager::get_instance().get_driver();
while (true) {
TaskQueue::Core0::process_tasks();
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i) {
device_driver->process(i, _gamepads[i]);
}
tud_task();
sleep_ms(1);
}
}
// #else // OGXM_BOARD == PI_PICO || OGXM_BOARD == RP2040_ZERO || OGXM_BOARD == ADAFRUIT_FEATHER
// void standard::host_mounted(bool host_mounted) {}
// void standard::initialize() {}
// void standard::run() {}
#endif // OGXM_BOARD == PI_PICO || OGXM_BOARD == RP2040_ZERO || OGXM_BOARD == ADAFRUIT_FEATHER

View File

@@ -0,0 +1,9 @@
#pragma once
#include <cstdint>
namespace standard {
void initialize();
void run();
void host_mounted(bool mounted);
} // namespace standard

Some files were not shown because too many files have changed in this diff Show More