The Nintendo Joystick Controller

==============================================================================

Useful information about the Nintendo joystick and driving software.
 
						Mark Harris Oct '97

==============================================================================


Introduction
============

The Nintendo joystick was chosen by Alwyn as a device to control movements 
in O. Christer built some electronics to produce RS232-type serial 
signals from it, and I have written a driving routine that generates
X events that can be interpreted by other X programs.

Christer's box monitors around 15 functions of the joystick controller,
including X and Y movement and button pushing, and the values of these 
functions are encoded into 4 packets each of which is an 8-bit byte. 
A packet is sent any time the value of a function encoded in that byte 
is set, and it is sent alone, without the other three bytes.
So normally the packets labelled 0 and 1 will be transmitted since these
happen to contain the Y and X coordinate values from the joystick itself,
whereas packets 2 and 3 will only be sent when certain buttons are pressed.
The driving routine monitors these incoming packets and stores the data
in a structure that contains 20 bytes and is transmitted as part of
an X client message. The status of all functions is sent whenever a packet
is received, even though only a quarter of the information has been updated.
No packet is sent when a button is released, so the values of these functions
are set to timeout after 4 packets that do not set them high have arrived,
or if the 10ms read timeout occurs because no data was recieved from the box.
The routine is called joystick.c, and compiles on DEC Alpha and SGIs.
The name of the the terminal port is read from the env variable JOYSTICK_PORT.
Useful runtime options include :
'c'  to see confirmation of data being sent
'tN' to throw away all but every Nth data point
'mN' to set the maximum number of identical data points before reseting
'zN' to set the max number of identical near-zero data points before reseting
'h'  to get help on the less useful runtime options

Data structures 
===============

The data arrives in 'my_event' as follows :

my_event.xclient.data.b[byte]

byte	contents
----	--------

0	The number of the packet most recently recieved (0-3)
1	The X coordinate of the joystick position (-80 to +80)
2	The Y coordinate of the joystick position (-80 to +80)
3	Internal use
4	Trigger switch (0 or 1)
5	Internal use
6	C-> button (0 or 1)
7	
8	<-  button (0 or 1)
9	->  button (0 or 1)
10	L   button (0 or 1)
11	
12	
13	C^  button (0 or 1)
14	C<- button (0 or 1)
15	V   button (0 or 1)
16	^   button (0 or 1)
17	
18	
19	77  (identifies client message as coming from Nintendo joystick)




Incorporating into existing code
================================

You need to call the driving program something like :

--------------------------------------------
fork_pid = fork();
if (fork_pid == 0) {
 execlp("joystick","joystick",0,0); 
 exit();
}
--------------------------------------------

Then it can be killed later with :

--------------------------------------------
kill (fork_pid,SIGKILL);
--------------------------------------------

The X event is dealt with as follows :

--------------------------------------------
while (1) {

  XNextEvent(display,&my_event);

  switch (my_event.type) {

  case ClientMessage:
    x = my_event.xclient.data.b[1];
    y = my_event.xclient.data.b[2];
    break;

  }
}	
--------------------------------------------


Problems
========

  The joystick only reveals information about relative motion, so someone
needs to keep track of its absolute position. This was once done
in the driving software, so whenever the value "1" was passed in a downward
direction, a sign bit is set so that we know that we are passing into the 
negative sector. Unfortunately, sometimes the hardware forgot to mention 
that it had passed this point, and so the absolute positioning failed.
This particularly happened if the stick is moved fast. 
The clock was wound up so that position data was sent more often, 
but it still failed sometimes. As an extra workaround, if a small value 
suddenly jumped to a large value, it was assumed that zero has been passed,
but this was not always true.
So, the number of bits representing the position was reduced, so that 
unique numbers could be used in the positive and negative sectors 
viz. 1 to 7 positive and 15 to 8 negative. 
This helped a lot, but there is the related problem of joystick drift, 
where small values are transmitted when the stick is really at rest. 
To avoid this the driver does not bother to send the values 1 and -1, 
but drift of 2 or 3 also happens. Therefore an automatic reset is implemented,
triggered by a number of identical, non-zero coordinate values.
This value is currently set at 500, which is equivalent to around 2 seconds
on a DEC-Alpha, but will presumably need to be changed for other hardware,
or if the joystick clock is changed.
Thats for values between 3 and 6. Values below 3 are reset much faster,
on the grounds that the joystick is probably at rest, and there is less danger
that it is just being held still by the user. Values over 6 never cause a reset
because it probably means the user has the stick up against the physical
boundary. It's all a question of probabilities - small numbers are very likely
to be drift, and so can be dealt with quickly, medium values could be real 
data, so we give the user more chance. Large values (against the stop)
are a very likely to be real user data, and very unlikely to be drift, 
so we let them pass every time.
Alternatively a hardware reset with the red 'start' button can be used, 
or a software reset which is currently sent from the driving program when 
the trigger is pulled. The latter will presumably be changed, since the 
trigger is probably of better service doing something else. 
The application program can easily do this reset by opening the terminal
port and writing any characters to it.
There appears to be no conflict with the port already being open for write
by the driving routine.

21-Oct-97

That system wasn't quite good enough, so a new system was built where
the software recieves the signals directly from the joystick sensors,
so that more complex processing can be done than was possible in hardware.
Each sensor transmits a series of pulses on two channels, the phase 
relationship between the two channels encoding the direction of movement,
and the number of pulses the distance travelled.
Unfortunately these tidy signals become erratic when the joystick is 
moved quickly, and it no longer becomes possible to accurately track
either distance or direction. When this occurs the software assigns
a score to the signal quality, from 1 (ideal) to 16 (random).
Decisions on reliability of data can then be made according to the 
duration and magnitude of the noise. For example, letting the stick 
fly back to the origin under the force of the springs gives noise that 
can be identified by noise above level 8 for more than 3 cycles.

Mark Harris 1998

(Back to MRH notes)