diff --git a/docs/index.rst b/docs/index.rst index ab932197..30bfa037 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -9,6 +9,7 @@ PS Move API Documentation Camera Network (Move Daemon) Configuration + Navigation Controller Mailing List diff --git a/docs/navcon.rst b/docs/navcon.rst new file mode 100644 index 00000000..6460ca96 --- /dev/null +++ b/docs/navcon.rst @@ -0,0 +1,98 @@ +Navigation Controller +===================== + +While the "PS Move Navigation Controller" (CECH-ZCS1E) is marketed +together with the Move Controller, in reality it's just a special +variant of the SixAxis / DualShock 3 controller with its own PID. + +* Vendor ID: **0x054c** (Sony Corp.) +* Product ID: **0x042f** (PlayStation Move navigation controller) + +As such, the Navigation Controller does not need a special library +for reading, processing and interpreting input events. After pairing, +the controller can be used just like a normal gamepad device via +the operating system game controller APIs. + + +Pairing +------- + +The ``psmove`` command-line utility now comes with a new sub-command +``pair-nav``, which will use ``sixpair`` to pair a Navigation Controller +(as well as a Sixaxis, DS3 and DS4) via USB. ``sixpair`` comes from the +`sixad`_ package, but has been modified to read the Bluetooth Host +address from the PS Move API libraries (for cross-platform support):: + + psmove pair-nav + +Just like with the normal pairing function, one can pass an alternative +Bluetooth host address to the pairing tool:: + + psmove pair-nav aa:bb:cc:dd:ee:ff + +.. _`sixad`: https://github.com/RetroPie/sixad + + +Testing +------- + +The easiest way to test the controller is via the ``jstest`` tool, +which comes in the ``joystick`` package on Debian-based systems:: + + sudo apt install joystick + +Assuming the Navigation Controller is the only joystick-style device +connected, you can test it with:: + + jstest /dev/input/js0 + +There is an example file ``examples/c/test_navcon.cpp`` that shows +how to use SDL2 to read the navigation controller (this is also +built as a program if ``PSMOVE_BUILD_NAVCON_TEST`` is enabled in CMake):: + + test_navcon + + +Axes and Buttons +---------------- + +These values are also exposed in ``psmove.h`` as ``enum PSNav_Button`` +and ``enum PSNav_Axis`` for your convenience. However, you are +expected to bring your own Joystick-reading library (e.g. SDL2). + + + * Analog stick + + * Axis 0 (horizontal, "X") + * Axis 1 (vertical, "Y") + * Button 7 when pressed ("L3") + + * Shoulder button ("L1"): Button 4 + * Analog trigger ("L2"): + + * Axis 2 ("Z") + * Button 5 when pressed (even just slightly) + + * Cross button: Button 0 + * Circle button: Button 1 + * D-Pad Up: Button 8 + * D-Pad Down: Button 9 + * D-Pad Left: Button 10 + * D-Pad Right: Button 11 + * PS button: Button 6 + + +Caveats +------- + +On Linux, all inputs work via both USB and Bluetooth. When connecting +via USB, you might need to press the PS button for the controller to +start reporting. + +On macOS, inputs only work over Bluetooth, not via USB. Also, the +button assignment is different on macOS (but ``psmove.h`` contains +definitions for both platforms and will pick the right ones). As a +special case, the analog value of the trigger isn't available on +macOS at the moment. + +This is not tested at all on Windows, contributions welcome. diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 00b80fb4..0c5a325a 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -7,6 +7,7 @@ include(${ROOT_DIR}/cmake/common.cmake) option(PSMOVE_BUILD_EXAMPLES "Build the C examples" ON) option(PSMOVE_BUILD_OPENGL_EXAMPLES "Build the OpenGL examples" ON) option(PSMOVE_BUILD_TESTS "Build the C tests" ON) +option(PSMOVE_BUILD_NAVCON_TEST "Build the navcon C test" ON) # C examples if(PSMOVE_BUILD_EXAMPLES) @@ -38,6 +39,22 @@ if(PSMOVE_BUILD_EXAMPLES) endif() endif() +if(PSMOVE_BUILD_NAVCON_TEST) + find_package(SDL2 QUIET) + + if (SDL2_FOUND) + include_directories(${SDL2_INCLUDE_DIRS}) + add_executable(test_navcon ${CMAKE_CURRENT_LIST_DIR}/c/test_navcon.cpp) + target_link_libraries(test_navcon ${SDL2_LIBRARIES}) + set(INFO_BUILD_NAVCON_TEST "Yes (using SDL2 joystick API)") + else() + set(INFO_BUILD_NAVCON_TEST "No (SDL2 not found)") + set(PSMOVE_BUILD_NAVCON_TEST OFF CACHE BOOL "Disabling NavCon test -- SDL2 not found" FORCE) + endif() +else() + set(INFO_BUILD_NAVCON_TEST "No (disabled)") +endif() + # C test programs if(PSMOVE_BUILD_TESTS) if(PSMOVE_BUILD_TRACKER) @@ -78,3 +95,4 @@ message(" Additional targets") feature_use_info("C example apps: " PSMOVE_BUILD_EXAMPLES) feature_use_info("OpenGL examples: " PSMOVE_BUILD_OPENGL_EXAMPLES) feature_use_info("C test programs: " PSMOVE_BUILD_TESTS) +message(" NavCon test: " ${INFO_BUILD_NAVCON_TEST}) diff --git a/examples/c/test_navcon.cpp b/examples/c/test_navcon.cpp new file mode 100644 index 00000000..e1d39479 --- /dev/null +++ b/examples/c/test_navcon.cpp @@ -0,0 +1,180 @@ + + /** + * PS Move API - An interface for the PS Move Motion Controller + * Copyright (c) 2020 Thomas Perl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + **/ + + + +#include +#include +#include + +#include + +#include "SDL.h" +#include "psmove.h" + +static int +find_nav_controller(int count) +{ + for (int i=0; i z) { + printf("#"); + } else { + printf("_"); + } + } + printf("]\n"); + size_t i=0; + for (int y=0; y<7; ++y) { + printf(" |"); + for (int x=0; x<11; ++x) { + if (ypos == y && xpos == x) { + printf("X"); + } else { + printf(" "); + } + } + printf("| "); + + for (int k=0; y>0 && k<2 && i < sizeof(BUTTONS)/sizeof(BUTTONS[0]); ++k) { + printf("[%c] %-10s ", + SDL_JoystickGetButton(joy, BUTTONS[i].index) ? 'x' : '_', + BUTTONS[i].name); + ++i; + } + + printf("\n"); + } + printf(" +-----------+ Analog Stick: (x=%+.1f, y=%+.1f)\n", + (float)x/SHRT_MAX, (float)y/SHRT_MAX); + + printf("\npress the [PS] button for 500 ms to exit..."); + + fflush(stdout); + SDL_Delay(10); + + printf("\n\033[11A"); + } + + printf("\033[11BPS button longpress -- exiting\n"); + } else { + printf("Couldn't open Joystick 0\n"); + } + + // Close if opened + if (SDL_JoystickGetAttached(joy)) { + SDL_JoystickClose(joy); + } + + SDL_Quit(); + + return 0; +} diff --git a/include/psmove.h b/include/psmove.h index e10121ef..c087ef8f 100644 --- a/include/psmove.h +++ b/include/psmove.h @@ -117,6 +117,55 @@ enum PSMove_Button { #endif }; +/*! Navigation Controller buttons. + * + * Note that the button assignment seems to be different on macOS + * compared to Linux. Untested on Windows, contributions welcome. + * + * Use these values to index into e.g. SDL Joystick Buttons. + **/ +enum PSNav_Button { +#if defined(__APPLE__) + NavBtn_CROSS = 14, /*!< Cross button */ + NavBtn_CIRCLE = 13, /*!< Circle button */ + + NavBtn_L1 = 10, /*!< Shoulder button */ + NavBtn_L2 = 8, /*!< Trigger */ + NavBtn_L3 = 1, /*!< Analog stick */ + + NavBtn_PS = 16, /*!< PS button */ + + NavBtn_UP = 4, /*!< D-Pad UP */ + NavBtn_DOWN = 6, /*!< D-Pad DOWN */ + NavBtn_LEFT = 7, /*!< D-Pad LEFT */ + NavBtn_RIGHT = 5, /*!< D-Pad RIGHT */ +#else + NavBtn_CROSS = 0, /*!< Cross button */ + NavBtn_CIRCLE = 1, /*!< Circle button */ + + NavBtn_L1 = 4, /*!< Shoulder button */ + NavBtn_L2 = 5, /*!< Trigger */ + NavBtn_L3 = 7, /*!< Analog stick */ + + NavBtn_PS = 6, /*!< PS button */ + + NavBtn_UP = 8, /*!< D-Pad UP */ + NavBtn_DOWN = 9, /*!< D-Pad DOWN */ + NavBtn_LEFT = 10, /*!< D-Pad LEFT */ + NavBtn_RIGHT = 11, /*!< D-Pad RIGHT */ +#endif +}; + +/*! Navigation Controller axes. + * + * Use these values to index into e.g. SDL Joystick Axes. + **/ +enum PSNav_Axis { + NavAxis_X = 0, + NavAxis_Y = 1, + NavAxis_Trigger = 2, /*!< might not work on macOS */ +}; + /*! Frame of an input report. * Each input report sent by the PS Move Controller contains two readings for diff --git a/include/psmove_config.h.in b/include/psmove_config.h.in index 84e78586..c76c1485 100644 --- a/include/psmove_config.h.in +++ b/include/psmove_config.h.in @@ -33,6 +33,7 @@ #cmakedefine PSMOVE_BUILD_TRACKER #cmakedefine PSMOVE_USE_PSEYE +#cmakedefine PSMOVE_USE_SIXPAIR /* Version information */ #define PSMOVEAPI_VERSION_MAJOR @PSMOVEAPI_VERSION_MAJOR@ diff --git a/scripts/install_dependencies.sh b/scripts/install_dependencies.sh index ae817cf0..1168593a 100644 --- a/scripts/install_dependencies.sh +++ b/scripts/install_dependencies.sh @@ -17,7 +17,8 @@ case "$UNAME" in python-dev mono-mcs \ swig3.0 freeglut3-dev \ libxrandr-dev libxinerama-dev libxcursor-dev \ - python-sphinx python-pip + python-sphinx python-pip \ + libusb-dev libsdl2-dev # Workaround to get BlueZ 5 on Travis CI (it doesn't yet have Ubuntu 16.04) # Based on: https://askubuntu.com/a/662349 @@ -33,7 +34,7 @@ case "$UNAME" in ;; Darwin) brew update - brew install --force cmake git libtool automake autoconf swig python || true + brew install --force cmake git libtool automake autoconf swig python libusb-compat sdl2 || true brew unlink libtool ; brew link --overwrite libtool pip2 install --user sphinx ;; diff --git a/scripts/macos/build-macos b/scripts/macos/build-macos index 1b6670ee..770074de 100644 --- a/scripts/macos/build-macos +++ b/scripts/macos/build-macos @@ -98,5 +98,6 @@ cmake -DPSMOVE_USE_PS3EYE_DRIVER=ON \ -DPSMOVE_BUILD_PYTHON_BINDINGS=OFF \ -DPSMOVE_BUILD_CSHARP_BINDINGS=OFF \ -DCMAKE_OSX_ARCHITECTURES="x86_64" \ + -DCMAKE_EXE_LINKER_FLAGS=-L/usr/local/lib \ -DOpenCV_DIR="${OPENCV_BUILD_DIR}" .. make ${MAKE_ARGS} diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index ad4b2482..a92aebfc 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -9,3 +9,15 @@ add_executable(psmove ${CMAKE_CURRENT_LIST_DIR}/psmovecli.cpp) target_link_libraries(psmove psmoveapi) set_property(TARGET psmove PROPERTY FOLDER "Utilities") list(APPEND PSMOVEAPI_INSTALL_TARGETS psmove) + +option(PSMOVE_USE_SIXPAIR "Enable Navigation Controller pairing" ON) + +if (WIN32) + set(PSMOVE_USE_SIXPAIR OFF CACHE BOOL "Disabling Navigation Controller pairing on Windows" FORCE) +else() + # sixpair needs libusb + find_package(PkgConfig REQUIRED) + pkg_check_modules(LIBUSB REQUIRED libusb) + include_directories(${LIBUSB_INCLUDE_DIRS}) + target_link_libraries(psmove ${LIBUSB_LIBRARIES}) +endif() diff --git a/src/utils/psmovecli.cpp b/src/utils/psmovecli.cpp index 00b803ae..d2a97bf2 100644 --- a/src/utils/psmovecli.cpp +++ b/src/utils/psmovecli.cpp @@ -89,6 +89,14 @@ extern "C" { } #undef main +#if defined(PSMOVE_USE_SIXPAIR) +#define main sixpair_main +extern "C" { +#include "sixpair.c" +} +#undef main +#endif /* PSMOVE_USE_SIXPAIR */ + static int usage(const char *progname, std::vector &subcommands) { @@ -220,6 +228,10 @@ main(int argc, char *argv[]) subcommands.emplace_back("extensions", "Show sharp shooter and racing wheel extension data", test_extension_main); subcommands.emplace_back("list", "List connected controllers", list_main); +#if defined(PSMOVE_USE_SIXPAIR) + subcommands.emplace_back("pair-nav", "Pair navigation controller via USB (using sixpair)", sixpair_main); +#endif /* PSMOVE_USE_SIXPAIR */ + subcommands.emplace_back(nullptr, "Debugging Tools (for developers)", nullptr); subcommands.emplace_back("dump-calibration", "Show the stored calibration information", dump_calibration_main); diff --git a/src/utils/sixpair.c b/src/utils/sixpair.c new file mode 100644 index 00000000..3c7d592e --- /dev/null +++ b/src/utils/sixpair.c @@ -0,0 +1,169 @@ +/* + * Modified 2020-03-11 by Thomas Perl for PS Move API + * + * Copyright (c) 2007, 2008 pascal@pabr.org + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#define VENDOR 0x054c +#define PRODUCT_SIXAXIS 0x0268 +#define PRODUCT_NAVIGATION 0x042f +#define PRODUCT_DS4 0x05c4 + +#define USB_DIR_IN 0x80 +#define USB_DIR_OUT 0 + +static void fatal(const char *msg) { perror(msg); exit(1); } + +void show_master(usb_dev_handle *devh, int itfnum, unsigned char *msg) { + printf("Current Bluetooth master: "); + int res = usb_control_msg + (devh, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0x01, 0x03f5, itfnum, (char *)msg, 8, 5000); + if ( res < 0 ) { perror("USB_REQ_GET_CONFIGURATION"); return; } + printf("%02x:%02x:%02x:%02x:%02x:%02x\n", + msg[2], msg[3], msg[4], msg[5], msg[6], msg[7]); +} + +void set_master(usb_dev_handle *devh, int itfnum, int mac[6]) { + printf("Setting master bd_addr to %02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + char msg[8]= { 0x01, 0x00, (char)mac[0],(char)mac[1],(char)mac[2],(char)mac[3],(char)mac[4],(char)mac[5] }; + int res = usb_control_msg + (devh, + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0x09, + 0x03f5, itfnum, msg, sizeof(msg), + 5000); + if ( res < 0 ) fatal("USB_REQ_SET_CONFIGURATION"); +} + +void process_device(int argc, char **argv, struct usb_device *dev, + struct usb_config_descriptor *cfg, int itfnum) { + int mac[6]; + + usb_dev_handle *devh = usb_open(dev); + if ( ! devh ) fatal("usb_open"); + +#ifndef __APPLE__ + usb_detach_kernel_driver_np(devh, itfnum); + + int res = usb_claim_interface(devh, itfnum); + if ( res < 0 ) fatal("usb_claim_interface"); +#endif + + unsigned char msg[8]; + show_master(devh, itfnum, msg); + + if ( argc >= 2 ) { + if ( sscanf(argv[1], "%x:%x:%x:%x:%x:%x", + &mac[0],&mac[1],&mac[2],&mac[3],&mac[4],&mac[5]) != 6 ) { + + printf("usage: %s []\n", argv[0]); + exit(1); + } + } else { + const char *addr = psmove_port_get_host_bluetooth_address(); + if (sscanf(addr, "%x:%x:%x:%x:%x:%x", + &mac[0],&mac[1],&mac[2],&mac[3],&mac[4],&mac[5]) != 6 ) { + printf("Unable to retrieve local bd_addr.\n"); + exit(1); + } + } + + bool matching = true; + for (size_t i=0; inext ) { + struct usb_device *dev; + for ( dev=bus->devices; dev; dev=dev->next) { + struct usb_config_descriptor *cfg; + for ( cfg = dev->config; + cfg < dev->config + dev->descriptor.bNumConfigurations; + ++cfg ) { + int itfnum; + for ( itfnum=0; itfnumbNumInterfaces; ++itfnum ) { + struct usb_interface *itf = &cfg->interface[itfnum]; + struct usb_interface_descriptor *alt; + for ( alt = itf->altsetting; + alt < itf->altsetting + itf->num_altsetting; + ++alt ) { + if ( dev->descriptor.idVendor == VENDOR && + (dev->descriptor.idProduct == PRODUCT_SIXAXIS || + dev->descriptor.idProduct == PRODUCT_NAVIGATION || + dev->descriptor.idProduct == PRODUCT_DS4) && + alt->bInterfaceClass == 3 ) { + process_device(argc, argv, dev, cfg, itfnum); + ++found; + } + } + } + } + } + } + + if ( ! found ) { + printf("No controller found on USB busses. Please connect your joystick via USB.\n"); + return 1; + } + return 0; +} +