Vous êtes sur la page 1sur 6

Asim Mittal © 2010

Wii + Python = Intuitive Control @ PyCon India '10


(The stuff that got left out...)

I know there might be a lot of you who hope to use the CwiiD project in revitalizing your applications. This
document will prove extremely useful to those looking for a starting point in this direction.
My talk at PyCon was essentially a demonstration of what I had conjured up with a few lines of code, two
months of spare time and a little imagination. Let it be known, that the applications demonstrated at PyCon
were purely for didactical purposes only.
This document will discuss in depth, the source code and how to use it in your applications. It will achieve this
goal using the applications, demonstrated at PyCon, as templates, upon which you may build your own. Only
the first three applications, demonstrated at PyCon will be explained. I feel that they are enough to create a
foundation for any enthusiast to understand the others or build your own.
The applications increase in complexity in the GUI, so a sound foundation in PyQt4 might prove to be
extremely useful. The games (bounce and paddleWar) and the Touchless workspace (puzzle) have highly UI
intensive code. If you need clarification on the UI, please feel free to contact me. When going through this
document, please have a copy of the source code at hand.
Please note that in order to run all the source files successfully on Ubuntu 8.10 or higher, you will need the
following packages installed:
a. python-cwiid
b. python-qt4

c. xautomation

Application 1: simpleConnect.py
The goal of this application was to demonstrate how to establish connectivity with the Wiimote, and start
reading the data through a pseudo event like structure.
Statement: wiiobj = wiigrab.WiimoteEventGrabber(someEventHandler,1)
The wiigrab.WiimoteEventGrabber class allows you to create a wiimote object. The arguments to this
constructor are as follows:
• handler = this is the callback routine that will be called to handle the events raised by the wiimote

• interval = this is the rate (in seconds) at which the wiimote events will be brought to the application, or
in simple terms, the rate at which the 'handler' will be called to process the reports sent from the
wiimote. Default value is 0.001 s (this means the report will be handled every millisecond). Based on
the nature of your application, this value may increase. Anything faster than this rate, may cause
excessive CPU usage and is not recommended
• objTemp = this is basically a dummy argument, which may or may not be used. The reason I created
this was to allow the application to pass arbitrary arguments to the handler. Sometimes you might
want to pass objects, tuples, lists, strings etc to the handler and use it therein. You may utilize this
objTemp to do exactly that. Default value is “None”

asim.mittal@gmail.com | http://baniyakiduniya.blogspot.com
Asim Mittal © 2010

The next thing that you have to do is set the type of report for the wiimote object you have just created. You
may notice the statement wiiobj.setReportType. For the most part, you wouldn't worry too much about the
arguments here. But the utility of this routine is to filter the report generated from the wiimote. If your
application only needs the button presses and doesn't care about any other info in the report, then you may
choose to pass rptButton=True to this function.
It imposes a preliminary filter on three major report types:
• rptButton
• rptAccel
• rptIR
The default value for all these boolean arguments is “True”, so if you don't pass any arguments to the
setReportType method, then the entire report is pumped into the handler. I use that as a general practice and
filter out the report using the dictionary keys in the handler. But if you feel that your app needs to conserve on
memory or CPU time, then by all means pass the appropriate argument(s) to this method.

The next statement you will see is wiiobj.led = 15. Here's a little background about the LEDs on the wiimote.
There are 4 LEDs on each wiimote . So the values range from 0 to F (hex) viz. 0 to 15 (decimal). I have not made
the LED assignment bit addressable but if you feel the need to do so, please write a small function which may
look something like setLed(True, False, True, True) and have a resolution based on the arguments.
I have used the LEDs purely for indicating, that the connectivity between the app and the wiimote are
established and that the app is ready. I'm sure there might be many apps where you may find a more creative
utility for these LEDs

You may notice that these three statements have been placed in a try-except block. This is in the event that no
wiimote is found, an exception is raised. If the wiimote pipe wasn't disconnected properly, there is a chance
that your port is blocked and you'll have to end up rebooting (this is a rare case, but I've seen it happen). So
this block allows the application to terminate properly in any case of failure when connecting to the wii
remote.
In case all goes well, the statement wiiobj.start() tells the wiimote to start sending reports to the app. Now
you must understand that the wiimote object here starts a thread which periodically invokes the handler. This
thread needs to share CPU time with your main routine (in case of a GUI based app, the wiimote thread will
run in parallel with the GUI event loop). In order to do so, we can simply run an infinite loop with a simple
sleep statement.
Again, there is a try-except block around this infinite loop. This is put to capture the “CTRL + C” (break)
exception and terminate the pipe between the app and the wiimote using the wiiobj.join() statement.
Now let's look at the “handler”. If you notice, I have written a simple function called someEventHandler,
which does two things:
• prints the report that has come from the wiimote
• reads the current LED status of the remote and toggles it (in order for the toggling of the LED to be
legible, I have used 1 second as the period for handler invocation. Most real apps would want to read
the reports from the wiimote a lot faster)
What is important here is that I am actually using the 'led' key to access the report (which is a dictionary of
such keys and associated values).

asim.mittal@gmail.com | http://baniyakiduniya.blogspot.com
Asim Mittal © 2010

Other keys may include: 'accel', 'ir_src', 'buttons' etc


That concludes the explanation of the first application. From this point on, I'll focus more on the handlers
rather than the main routine of every app as the steps involved are the same (specially for the console based
applications)

Application 2: simpleButtons.py
The main routine is similar to the previous application, the only difference being that there is no interval
specified in the constructor call. This means I'll be sampling reports from the wiimote at ever millisecond.
The goal of this application is to demonstrate how buttons can be used to do something useful. The app also
demonstrates how you can resolve simultaneous button presses from each other. Before we start discussing
the app in more detail, you must understand what a button press means. Whenever a button is pressed on the
wiimote, the value against the 'buttons' key changes. Each button on the wii remote has an integer value
(ranging from 4 to 4096). There is a very sound reason for this wide range of values. When you press more
than one button together, the “sum of the individual values” of each button is dumped in the report.
For example, button A = 4 and button B = 8 (assume, i'm a bit fuzzy about the values). When you press both
buttons together, the value against 'buttons' key is 12.
Now lets take a look at the report handler. You can see that I get the value of button by accessing the 'buttons'
key in the report dictionary. The next line is really important. If multiple buttons are pressed, you can use
wiigrab.resolveButtons(buttonValue) to resolve each key. The logic of the resolver routine may be found in
the wiigrab.py file. I will not discuss that here as it is beyond the scope of this document. This resolver routine
returns a list containing all the numeric values of the keys that were pressed.
The application then proceeds to check the number of buttons pressed. If more than two buttons were
pressed it sends a feedback to the remote using the vibrator. You can control the vibrator using the rumble
property. This property takes two values (0 or 1) to indicate off or on states respectively.
Please note that the rumble operation works like a switch. If you turn on the vibration, you are responsible to
turn it off (notice the else clause with the if clause). If you don't explicitly turn off the vibrator, the remote will
continue buzzing.
Another thing the report handler does is get the name of each button in the resolved values. This is done using
the wiigrab.getNameOfButton(value) routine.
All this info is printed on the screen. And that is how you work with buttons on the Wiimote!!

Application 3: runDualPointDemo.py
This is the application that demonstrates the IR tracking ability of the wiimote. This is a very important app as
it demonstrates two important concepts:
• Using the wiimote with an app that has a GUI
• implementing the concept of “iterative displacement”
First, let me focus on how the application's GUI is designed. Its really simple. You may find that GUI has a
window with a red square and a blue square. Both these are painted using the draw() method of the
IRTracking class. The positions of the squares are saved in the members self.posBlue & self.posRed.
The next thing that you must note in the code is the event binding associated with the form.
SIGNAL(“moveDot”) is tied up to the self.move() event handler. Now if you look closely at this handler, you'll
see that it takes three arguments viz.

asim.mittal@gmail.com | http://baniyakiduniya.blogspot.com
Asim Mittal © 2010

• Dot = indicates whether you want to move the red or blue dot. If value is 0, then it refers to the red
dot, if 1 to the blue dot

• x = indicates the displacement you want along the x – axis for the specified dot

• y = indicates the displacement you want along the y – axis for the specified dot

All that this routine does is displace the red/blue dot along the x and y axes by the specified amount. Note,
that the values in self.posBlue and self.posRed are altered. But the “illusion” of movement on the form is taken
care of by the draw() routine. So whenever this signal is generated, the new positions of the specified dot is
computed and the draw routine will repaint the form. The entire process is so fast that to the naked eye it
seems like smooth motion across the area of the form.
Now what does the wiimote really do here?? Its simple... it generates the “MoveDot” signal. Go straight down
to the handleReportDualPointTracking() routine. The routine is a typical report handler, and it is defined
outside the body of the IRTracking class. The routine is passed to the form in the main routine of the
application. The main routine of the app creates the form and starts the Qt event loop. So the wiiobject is
created as part of the members of the IRTracking class, in the initWiimote() method.
The form's reference is passed to the handler using the objTemp argument. In this manner, the handler can
exercise control over the properties and objects of the form. Please note, this may not be the best practice
and certainly is not the most thread-safe way to go about doing this. But since my application doesn't
change any physical attributes of the form and there is limited user interactivity, it works. Ideally, in
the case of a larger application, you should make the report handler a part of the class to avoid
segmentation faults of any kind.
Moving onto the internals of the report handler. The IR points of the report can be got in a tangible form using
the wiigrab.getVisibleIRPoints() method. This routine returns two easy to use objects. The first one is a list
and the second, is a number (positive).
The list will always have FOUR members that indicate the positions of the four points any wiimote can track. A
wiimote cannot track more than four points. If your wiimote cannot see any IR points, the list looks like:
[None,None,None,None]
if you have a single point being tracked, the list will look like:
[wiipoint_object, None, None, None]
similarly, as more points are visible, the list changes accordingly. Point 1 is represented by the first element
and point 4 by the last. Since the size of the list doesn't change, in order to aid processing, the second value
returned by the routine is the “number of visible points”
You can see that the code following this is only a bunch of if – else clauses that check the second value
returned for the no. of points visible.
TIP: Please read the wiipoint class description to understand how wiipoint_objects (the members of the
list of IR points) work. They're simple wrappers which basically indicate the positions as tuples (x,y).
The value of any IR point can range from (0,0) to (1024,768). The reason I wrote the wiipoint class was
to accommodate basic cartesian math operations. You may extend the features of this class as you see
fit.
Now lets proceed towards understanding the concept of “iterative displacement”. In order to move the dot
(red/blue) on the form what you need is:
• info about the latest positions of the dots
• info about the latest positions of the IR points

asim.mittal@gmail.com | http://baniyakiduniya.blogspot.com
Asim Mittal © 2010

What we basically do is track the successive displacement of the IR points. Scale this displacement and displace
the red and blue dots by the scaled value. Another thing you want to pay attention to is understanding how to
move dots from rest postions.
Now in order to get the positions of the dots, we can use the lists self.posRed/self.posBlue. And in order to
track the positions of the IR points we've made another list (check initWiimote method) called self.oldCoords.
This is nothing but a list of size two. Each member is a wiipoint type object. The reason we have two members
is because we have two dots on our current form. Each wiipoint object will map to a particular dot.
Lets first consider the case when there is at least one IR point visible in space (else clause in report handler).
The for loop ranges over the number of visible dots in the list rptIR. So this means when two dots are visible,
the loop will run twice and if only one dot is visible, the loop will run once. What is important is the value of “i”
in this loop. Now rptIR contains the “current” position of the IR points, and self.oldCoords contains the last
known position. The difference between these points can give you the displacement of the IR point from its
last known position along X and Y directions. All we do then is scale that displacement and emit the “moveDot”
signal.
The Qt Signal and Slot framework takes care of the rest. It recognizes that a signal that is registered with this
form is generated and immediately invokes the associated slot viz. Self.moveDot() routine. The values of
displacement that were computed are sent as arguments and “i” indicates the dot being moved. When I = 0, we
refer to IR Point 1 (tied up to the red dot) and when I = 1, we refer to IR Point 2 (tied up to the blue dot).
You may notice that there is an if clause that checks the size of the IR Point and generates the “moveDot”
signal. There is a sound logic behind that. You want the dots to move smoothly from the rest position.
Whenever the IR points dissappear, the code in the if clause is invoked:
if countVisible == 0:

frmRef.oldCoords[0].nullify()

frmRef.oldCoords[1].nullify()

The nullify routine, makes the values of the wiipoint objects 0. That means X = 0, y=0 and Size = 0. Whenever
an IR point is detected by the wiimote, it has a size associated with it. This is done to give you an estimation of
“depth” (yes!! it has faint 3D info). This size parameter ranges between 1 to 7 (I think!). As the point comes
closer to the remote, the size increases and as you move the IR point further away from the remote, the size
value reduces.
In theory, the size value is never 0. If the dot is not visible, you will simply get “None”. So when I nullify the
values of the wiipoint objects in the oldCoords list. I'm using the “improbably value” of the size property of an
IR point to tell my application that the IR points aren't visible, and that means that the red and blue dots are
now going to be at “REST”.
So the if clause in the for loop skips if the dots were declared to be at rest. You may ask WHY??? simply
because the displacement computation in this case is going to be inaccurate. The old value of the IR Point (at
rest condition) was 0 (thanks to nullify). But this is an improbable value and therefore using this in
computation of displacement with any value that you may receive is wrong. So we skip it. The next line causes
the current value to become the old value for the next displacement computation, where in both values
represent actual positions of the IR Points recorded in space.
If this sounds cryptic and overtly confusing, understand this: if you don't check for the “rest” condition, your
red/blue dot will appear to jump in a jarring manner. To give it the smooth movement from its rest position,
we use this if clause to eliminate an incorrect condition.

asim.mittal@gmail.com | http://baniyakiduniya.blogspot.com
Asim Mittal © 2010

But I'm sure you get the idea. The report handler tracks displacement of IR points and emits a signal which
causes the dots on the form to be displaced by a similar amount. The direction of movement of IR points is
opposite to that of the dots, that is why you have to negate the displacement in the moveDot routine.

Help build a greener tomorrow! Join ektitli.org


today.
Find out more about what you can do to help
make your city greener. Visit http://ektitli.org

asim.mittal@gmail.com | http://baniyakiduniya.blogspot.com

Vous aimerez peut-être aussi