Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ PS Move API Documentation
Camera <camera.rst>
Network (Move Daemon) <moved.rst>
Configuration <config.rst>
Navigation Controller <navcon.rst>


Mailing List
Expand Down
98 changes: 98 additions & 0 deletions docs/navcon.rst
Original file line number Diff line number Diff line change
@@ -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.
18 changes: 18 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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})
180 changes: 180 additions & 0 deletions examples/c/test_navcon.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@

/**
* PS Move API - An interface for the PS Move Motion Controller
* Copyright (c) 2020 Thomas Perl <m@thp.io>
* 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 <stdio.h>
#include <stdlib.h>
#include <limits.h>

#include <algorithm>

#include "SDL.h"
#include "psmove.h"

static int
find_nav_controller(int count)
{
for (int i=0; i<count; ++i) {
const char *name = SDL_JoystickNameForIndex(i);

// Linux via USB
if (strcmp(name, "Sony Navigation Controller") == 0) {
printf("Found USB Navigation Controller at index %d\n", i);
return i;
}

// Linux via Bluetooth
if (strcmp(name, "Navigation Controller") == 0) {
printf("Found Bluetooth Navigation Controller at index %d\n", i);
return i;
}
}

return -1;
}

static const struct {
const char *name;
int index;
} BUTTONS[] = {
{ "CROSS", NavBtn_CROSS },
{ "CIRCLE", NavBtn_CIRCLE },

{ "L1", NavBtn_L1 },
{ "L2", NavBtn_L2 },
{ "L3", NavBtn_L3 },

{ "PS", NavBtn_PS },

{ "UP", NavBtn_UP },
{ "DOWN", NavBtn_DOWN },
{ "LEFT", NavBtn_LEFT },
{ "RIGHT", NavBtn_RIGHT },
};

int main(int argc, char* argv[])
{
SDL_Init(SDL_INIT_JOYSTICK);

int count = SDL_NumJoysticks();
if (count == 0) {
printf("No joysticks connected\n");
exit(1);
}

printf("Found %d joystick(s)\n", count);

int index = find_nav_controller(count);

if (index == -1) {
printf("Could not find a Navigation Controller\n");
exit(1);
}

printf("Found Joystick %d: %s\n\n", index, SDL_JoystickNameForIndex(index));

SDL_Joystick *joy = SDL_JoystickOpen(index);

int ps_count = 0;

if (joy) {
SDL_Event e;
while (ps_count < 50) {
SDL_JoystickUpdate();

if (SDL_JoystickGetButton(joy, NavBtn_PS)) {
++ps_count;
} else {
ps_count = 0;
}

Sint16 x = SDL_JoystickGetAxis(joy, 0);
Sint16 y = SDL_JoystickGetAxis(joy, 1);
Sint16 z = SDL_JoystickGetAxis(joy, 2);

int xpos = 5 + 5 * (int)x / SHRT_MAX;
int ypos = 3 + 3 * (int)y / SHRT_MAX;
int zpos = 10 + 10 * (int)z / SHRT_MAX;

printf(" +-----------+ Trigger: %+.2f [", (float)z/SHRT_MAX);
for (int z=0; z<20; ++z) {
if (zpos > 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;
}
49 changes: 49 additions & 0 deletions include/psmove.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions include/psmove_config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -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@
Expand Down
Loading