Wiiewer
From WiiLi
Wiiewer is a very simple Python script that plots the motion sensor data. I found WMD very powerful but also very difficult to modify and adapt. Wiiewer is just a short example that you can reuse for your own purposes.
Contents |
[edit] Screenshot
[edit] Code
#!/usr/bin/python # Copyright (c) 2006 Sam Hocevar <sam@zoy.org> # # This program is free software. It comes without any warranty, to # the extent permitted by applicable law. You can redistribute it # and/or modify it under the terms of the Do What The Fuck You Want # To Public License, Version 2, as published by Sam Hocevar. See # http://sam.zoy.org/wtfpl/COPYING for more details. import pygame from pygame.locals import QUIT from bluetooth import BluetoothSocket, BluetoothError, L2CAP import thread, time WIDTH = 512 HEIGHT = 256 # Don't change this one ADDRESS = '00:19:1D:88:56:BA' # Change this one, of course # Listening thread def listener(): global connected, sensor fdin.settimeout(0.1) while connected == 1: try: msg = fdin.recv(23) except BluetoothError: continue if len(msg) >= 7: for c in range(3): sensor[c] = ord(msg[4 + c]) fdin.close() fdout.close() connected = -1 # Try to connect to Wiimote print 'connecting to Wiimote ' + ADDRESS + ', please press buttons 1 and 2' fdin = BluetoothSocket(L2CAP) fdin.connect((ADDRESS, 0x13)) fdout = BluetoothSocket(L2CAP) fdout.connect((ADDRESS, 0x11)) if not fdin or not fdout: raise 'could not connect to Wiimote, check the address' # Open window pygame.init() window = pygame.display.set_mode((WIDTH, HEIGHT), 0) pygame.display.set_caption('Wiiewer') # Run listener old = [127] * 3 sensor = [127] * 3 connected = 1 thread.start_new_thread(listener, ()) fdout.send("\x52\x12\x00\x31") # Main display loop while connected == 1: pixels = pygame.surfarray.pixels3d(window) for c in range(3): for t in range(min(old[c], sensor[c]) + 1, max(old[c], sensor[c])): pixels[WIDTH - 3][t][c] = 255 pixels[WIDTH - 2][sensor[c]][c] = 255 old[c] = sensor[c] del pixels window.unlock() window.blit(window, (-1, 0)) pygame.display.flip() for ev in pygame.event.get(): if ev.type == QUIT: connected = 0 time.sleep(0.01) while connected == 0: time.sleep(0.01) pygame.display.quit()
[edit] With camera tracking
I kinda hacked around a bit with the above to work with the IR camera as well. It's horrible code, I know, but it does work.
#!/usr/bin/python
# Copyright (c) 2006 Sam Hocevar <sam@zoy.org>
#
# Camera code hacked in by Pete Ryland <pdr@pdr.cx> on 9 April 2008.
#
# This program is free software. It comes without any warranty, to
# the extent permitted by applicable law. You can redistribute it
# and/or modify it under the terms of the Do What The Fuck You Want
# To Public License, Version 2, as published by Sam Hocevar. See
# http://sam.zoy.org/wtfpl/COPYING for more details.
import pygame
from pygame.locals import QUIT
from bluetooth import BluetoothSocket, BluetoothError, L2CAP
import thread, time
WIDTH = 512
HEIGHT = 256 # Don't change this one
ADDRESS = '00:1E:35:E3:0A:F8' # Change this one, of course
# Listening thread
def listener():
global connected, sensor, buttons, last_buttons, ir
fdin.settimeout(0.1)
while connected == 1:
try:
msg = fdin.recv(23)
except BluetoothError:
continue
if ord(msg[1]) == 0x33:
#print 'recv' + ' '.join([ord(x).__hex__() for x in msg])
buttons = (ord(msg[2]) & 0x1f) + ((ord(msg[3]) & 0x9f) << 5)
if buttons != last_buttons:
button_string = ''
for i in xrange(13):
if buttons & (1 << i):
button_string += '<>v^+21BA-??H'[i]
print button_string
last_buttons = buttons
if len(msg) >= 7:
for c in range(3):
sensor[c] = ord(msg[4 + c])
for i in xrange(4):
ir[i].x = ord(msg[7+i*3]) + ((ord(msg[9+i*3]) & 0x30) << 4)
ir[i].y = ord(msg[8+i*3]) + ((ord(msg[9+i*3]) & 0xc0) << 2)
ir[i].s = ord(msg[9+i*3]) & 0x0f
#print i, ir[i].x, ir[i].y, ir[i].s
fdin.close()
fdout.close()
connected = -1
# Try to connect to Wiimote
print 'connecting to Wiimote ' + ADDRESS + ', please press buttons 1 and 2'
fdin = BluetoothSocket(L2CAP)
fdin.connect((ADDRESS, 0x13))
fdout = BluetoothSocket(L2CAP)
fdout.connect((ADDRESS, 0x11))
if not fdin or not fdout:
raise 'could not connect to Wiimote, check the address'
# Open window
pygame.init()
window = pygame.display.set_mode((WIDTH, HEIGHT), 0)
pygame.display.set_caption('Wiiewer')
# Run listener
old = [127] * 3
sensor = [127] * 3
buttons = 0
last_buttons = 0
class Ir:
x, y, s = 0, 0, 0
ir = [Ir(), Ir(), Ir(), Ir()]
last_ir = [Ir(), Ir(), Ir(), Ir()]
connected = 1
thread.start_new_thread(listener, ())
fdout.send("\x52\x12\x00\x33")
fdout.send("\x52\x13\x04")
fdout.send("\x52\x1a\x04")
fdout.send("\x52\x16\x04\xb0\x00\x30\x01\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
fdout.send("\x52\x16\x04\xb0\x00\x00\x09\x02\x00\x00\x71\x01\x00\x64\x00\xfe\x00\x00\x00\x00\x00\x00\x00")
fdout.send("\x52\x16\x04\xb0\x00\x1a\x02\xfd\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
fdout.send("\x52\x16\x04\xb0\x00\x33\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
fdout.send("\x52\x16\x04\xb0\x00\x30\x01\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
# Main display loop
while connected == 1:
pixels = pygame.surfarray.pixels3d(window)
for i in range(4):
if last_ir[i].x != 1023 and last_ir[i].y != 1023 and last_ir[i].s != 15:
(real_x, real_y) = ((((1023-last_ir[i].x)*WIDTH) >> 10), (last_ir[i].y*HEIGHT) >> 10)
if real_x >= 0:
pixels[real_x-1][real_y][0] ^= 255
pixels[real_x-1][real_y][1] ^= 255
pixels[real_x-1][real_y][2] ^= 255
for c in range(3):
for t in range(min(old[c], sensor[c]) + 1, max(old[c], sensor[c])):
pixels[WIDTH - 3][t][c] = 255
pixels[WIDTH - 2][sensor[c]][c] = 255
old[c] = sensor[c]
for i in range(4):
if ir[i].x != 1023 and ir[i].y != 1023 and ir[i].s != 15:
(real_x, real_y) = ((((1023-ir[i].x)*WIDTH) >> 10), (ir[i].y*HEIGHT) >> 10)
pixels[real_x][real_y][0] ^= 255
pixels[real_x][real_y][1] ^= 255
pixels[real_x][real_y][2] ^= 255
last_ir[i].x = ir[i].x
last_ir[i].y = ir[i].y
last_ir[i].s = ir[i].s
del pixels
window.unlock()
window.blit(window, (-1, 0))
pygame.display.flip()
for ev in pygame.event.get():
if ev.type == QUIT:
connected = 0
time.sleep(0.01)
while connected == 0:
time.sleep(0.01)
pygame.display.quit()
[edit] Similar code from WMD
[edit] Perhaps it could be made stand-alone?
Latest version at: https://svn.forthewiin.org/wmd/trunk/wmd/UI/PyGame.py
from wmd.Common import *
import time
class Grapher:
# These four values sould go into the configuration file
winheight = 800 # Window height at startup
width = 700 # Window width at startup
save_tga = 0 # Set this to 1 if you want to save the force log as a series of TGA files
bgcolor = (255,255,255) # Background color for the window
axcolors = [ (255,0,0), (0,255,0), (0,0,255) ] # Colors for each axis: x,y,z
sepcolor = (190,190,190) # Color for the separation lines
mmcolor = (25, 25, 25) # Color of the min/max text
# This is the width in number of pixels to use for every point in the graph.
tInc = 1.5 # Increase (use value between 1-4 for best results) to have a more faster, more precise graph
init_ignore = 10 # Number of frames to ignore completely while initializing
init_skip = 10 # Number of frames to use for calibration, without graphing them
blank_ahead_width = 80 # How many pixels should be blanked ahead of the new graph when redrawing?
vsep_period = 1.0 # Draw a vertical separator every how many seconds? Set to 0 to disable
fontheight = 20
fontpadding = 5
# You shouldn't have to touch these
headerheight = 2*(fontheight+fontpadding)
height = winheight - headerheight
init = init_skip + init_ignore
ok = 0
min = 120 # Initial calibration values
max = 160
t = 0 # Start graphing at the left side of the graph
id = 0
spf = 4 #Split factor
prev_ui_info_rect = 0
def __init__( self, ev ):
self.ev = ev
self.try_import()
if self.ok:
self.ev.subscribe( WM_ACC, self.ev_wm_acc ) # subscribe to wiimote force/accelerometer reports, handle with ev_wm_acc()
self.ev.subscribe( UI_INFO, self.ev_ui_info ) # subscribe to UI_INFO messages and display them, handle with ev_ui_info()
self.ev.subscribe( WMDPOWER, self.ev_wmdpower ) # let's use this to control graphing speed with ev_wmdpower()
# Initialize pygame screen with selected height, width, background colour; set background colour immediately
self.pygame.init()
self.init_font()
self.screen = self.pygame.display.set_mode( (self.width, self.winheight), self.pygame.RESIZABLE )
self.pygame.display.set_caption( "HELLO WORLD THIS IS WMD 0.1.2" )
self.screen.fill( self.bgcolor )
self.draw_separators()
self.pygame.display.flip()
# Register for event processing of these event types only. However, other events still seem to get through.
pgevlist = [ self.pygame.VIDEORESIZE, self.pygame.VIDEOEXPOSE ]
self.pygame.event.set_allowed( pgevlist )
def init_font( self ):
if self.pygame.font:
try:
self.font = self.pygame.font.Font("wmd/VeraMono.ttf", self.fontheight)
except:
try:
self.font = self.pygame.font.Font(None, self.fontheight)
except:
self.font = 0
log(LOG_ERR, "Cannot load font")
def try_import( self ):
try:
import pygame
self.ok = 1
self.pygame = pygame
except:
log(LOG_ERR, "Could not import pygame. Set DISABLE_PYGAME=1")
self.ok = 0
def scale( self, n ):
# Adaptive scaling. It should be modified to gradually revert back to a more precise scale
# if the force hasn't exceeded a certain peak for some time
sn = (n - self.min) * (self.height / (self.max - self.min))
return self.height -sn
def ev_wm_acc( self, force ):
# first 10 (by default) frames are ignored
if self.init > self.init_skip:
self.init -= 1
return
axes = [ 'x', 'y', 'z' ]
fv = [] # sfv: scaled force values
sfv = [] # ofv: old force values (from one wiimote tick ago)
for ax in axes:
v = force[ax]
fv.append( v )
sfv.append( self.scale( v ) )
self.graphForce( fv, sfv )
# graph the next set of x,y,z force values
def graphForce( self, fv, sfv ):
# frames 10-20 (by default) are used for calculating min/max values, but not graphed
# set self.init_skip to choose the number of frames to be skipped
if self.init > 0:
self.init -= 1
elif self.init == 0:
self.draw_minmax()
self.pygame.display.flip()
self.init -= 1
else:
self.draw_lines( sfv )
self.proc_events( )
# store x,y,z values for next line
self.ofv = sfv
# Store new mins and maxs for scaling purposes
for ax in fv:
if ax < self.min:
self.min = ax
elif ax > self.max:
self.max = ax
def draw_lines( self, sfv ):
# draw the lines
tInc = self.tInc # For every tick, let's increment t by this value. Higher t, faster scroll. t is used as the x-value
for i in range(3):
color = self.axcolors[i]
t = self.t
spf = self.spf # Split the window into four equal horizontal regions
y2 = self.ofv[i] /spf+(i*self.height/spf) # This is the y-value of the startpoint (it's also the previous endpoint)
y1 = sfv[i] /spf+(i*self.height/spf) # And this is the y-value of the endpoint of the aaline
yc2 = self.ofv[i] /spf+((spf-1)*self.height/spf) # And these are used for the combined display
yc1 = sfv[i] /spf+((spf-1)*self.height/spf)
# Let's draw anti-aliased lines
self.pygame.draw.aaline(self.screen, color, (t, y2), (t+tInc, y1), 1)
self.pygame.draw.aaline(self.screen, color, (t, yc2), (t+tInc, yc1), 1)
self.check_time()
updRect = self.pygame.Rect( t, 0, self.blank_ahead_width, self.height )
self.pygame.display.update( updRect )
# increment time counter (x-position on screen)
self.t += tInc
# if we have reached end of the screen
if self.t >= self.width:
self.end_of_cycle()
def check_time( self ): # Every second or so, draw a vertical line
if not self.vsep_period:
return
now = time.time()
if not self.t:
self.last_vsep = now
else:
if now > self.last_vsep + self.vsep_period:
self.last_vsep = now
t = self.t
self.pygame.draw.aaline( self.screen, self.sepcolor, (t,0), (t,self.height), 1)
def proc_events( self ):
## This processes events, and allows us to resize the window
pgev = self.pygame.event.poll()
while pgev:
if pgev:
if pgev.type == self.pygame.VIDEORESIZE:
self.width = pgev.w
self.winheight = pgev.h
self.height = self.winheight - self.headerheight
self.screen = self.pygame.display.set_mode( (self.width, self.winheight), self.pygame.RESIZABLE )
self.screen.fill( self.bgcolor )
self.draw_minmax()
self.draw_separators()
self.pygame.display.flip()
self.t = 0
self.init = 10
elif pgev.type == self.pygame.QUIT:
self.ev.send( EV_SHUTDOWN, '' )
pgev = self.pygame.event.poll()
def end_of_cycle( self ):
# PNG saving requires pygame >= 1.8, otherwise it can only save as TGA !
if self.save_tga:
self.pygame.image.save(self.screen, "wii%03d.tga" % self.id)
self.id += 1
self.t = 0
# clear screen
self.screen.fill( self.bgcolor )
self.draw_separators()
self.draw_minmax()
def draw_minmax( self ):
# draw min/max values at the top of the screen
if self.font:
textStr = "WMD Accelerometer Graph - Range: [%s, %s] " % (int(self.min) , int(self.max))
mmcolor = self.mmcolor
text = self.font.render(textStr, 1, mmcolor)
textpos = text.get_rect(centerx=self.width/2,top=self.height)
self.screen.blit(text, textpos)
self.pygame.display.update( textpos )
def ev_ui_info( self, msg ):
if self.font:
textStr = msg
mmcolor = self.mmcolor
text = self.font.render(textStr, 1, mmcolor)
textpos = text.get_rect(centerx=self.width/2,top=self.height + self.fontheight + self.fontpadding)
if self.prev_ui_info_rect:
self.screen.fill(self.bgcolor, self.prev_ui_info_rect)
self.screen.blit(text, textpos)
if self.prev_ui_info_rect:
self.pygame.display.update( self.prev_ui_info_rect )
else:
self.pygame.display.update( textpos )
self.prev_ui_info_rect = textpos
def ev_wmdpower( self, pwrchange ):
if (self.tInc + pwrchange) < 0:
return
else:
self.tInc += pwrchange
def draw_separators( self ):
spf = self.spf
sepcolor = self.sepcolor
for i in range(spf+1):
y = (i*self.height/spf)
self.pygame.draw.aaline( self.screen, sepcolor, (0, y), (self.width, y), 1)
Windows
cWiimote | GlovePIE | RMX Automation | Wiim | wiimote-api | WiinRemote | WiimoteLib
Linux
CWiid | WMD | Perlwiimote | libwiimote | lg3d-wii
OSX
DarwiinRemote | Remote Buddy | The Wiinstrument
Multiplatform
OpenPIE | Wiimote_Simple | WiiremoteJ | wiiuse | WiiJuce | WiiuseJ
PyBluez Scripts: Wiiewer | Wiimotecomm


