Page MenuHomePhabricator

No OneTemporary

diff --git a/wiipy_backend.py b/wiipy_backend.py
--- a/wiipy_backend.py
+++ b/wiipy_backend.py
@@ -1,774 +1,774 @@
#!/usr/bin/env python
"""
(c) 2009 Andreas Boehler, andreas.boehler@fh-linz.at, http://klasseonline.dyndns.org
-(c) 2009 Patrick Huebner, patrick.huebner@fh-linz.at
+
based on wiimote.py found online (through Google, doesn't seem to be published anymore)
Source: http://homepage.mac.com/wgwoods/wii/wiimote.py saved as wiimote_orig.py
v1.9.3, 2009/05/24
Feature: Added support for the Classic Controller
v1.9.2, 2009/05/22
Feature: Speed improvement by 100% by switching from simpleosc to pyliblo!
Drawback: pyliblo must be manually compiled on OS X
v1.9.1, 2009/05/18
Fix duplicate packet handling on OS X (again)
v1.9.0, 2009/05/18 - first version towards a 2.0.0 release
Completely reworked packet handling
Memory reading and writing should be more robust now
Reworked Initialization sequence
BalanceBoard initialization on OS X should work now
Fixed OS X compatibility by handling duplicate packets differently
v1.8.2, 2009/05/12
Feature: Nunchuk disconnection works properly
v1.8.1, 2009/05/12
Bugfix: BalanceBoard works now (IR must not be enabled on the BB; Setting the
registers seems to influence the BalanceBoard's sensitivity.
Note: On OS X the BalanceBoard sometimes does not work because of limitations
in the Perfect Pairing Patch of OSCulator. Author contacted!
v1.8, 2009/05/11
Bugfix: Connection with inserted nunchuk fixed
Bugfix: Restructure
Feature: Removed some cmd-line arguments, thus making backend invocation easier
Feature: Completely reworked initialization sequence
v1.7.5, 2009/05/11
Feature: Setup is now completely automatic, no need to configure anymore!
Note: When Nunchuk is connected upon startup, connection fails sometimes!
v1.7.4, 2009/05/09
Bugfix: Fix for BalanceBoard Extension Controller
Bugfix: Fix IR not working when Nunchuk connected (initialization error)
v1.7.3, 2009/05/07
Bugfix: Device not Found had a logical Error: it could never be reached
Documentation updates everywhere in the code
v1.7.2, 2009/05/06
Bugfix: Duplicate Devices found on Win32 are now removed
v1.7.1, 2009/05/06
Bugfix: Fixed OSC IR sending code
v1.7, 2009/05/05
Feature & Bugfix: Correctly handle duplicate packets (mainly on OS X)
v1.6.4, 2009/04/27
Bugfix: Fixed Device Discovery OSC Message on OS X (Unicode String Problem)
Cleaned up the code a bit
v1.6.3, 2009/04/25
Bugfix: Correctly exit on OS X
Feature: OSC Message if no device was found
v1.6.2, 2009/04/25
Bugfix: Correctly handle OS X connection error
v1.6.1, 2009/04/24
Bugfix: Make backend correctly handle CTRL-BREAK
v1.6, 2009/04/23
Feature: Added Discovery Routine
v1.5, 2009/04/21
Bugfix: Fixed OS X Compatibility
v1.4, 2009/04/20 (2:00 AM)
Feature: Added support for Nyko Wireless Nunchuk by Sniffing the original handshake
Feature: Added Status support
Feature: Added support for Datel Wirelesse Nunchuk
Feature: Insertion of a Nunchuk is now automatically detected and should be initialized automatically
v1.3, 2009/04/03
Feature: Moved existing code into Backend using Command-Line Parameters
v1.2, 2009/03/31
Feature: Added basic BalanceBoard support
Bug: Fixed Extension Controller initialization sequence
v1.1, 2009/03/17
Feature: Added Nunchuk-Connection Check
Feature: Added some more configuration variable on the top
Feature: Added Nunchuk Buttons -> Nunchuk is now FULLY SUPPORTED!
v1.0, 2009/03/17
Feature: Added IR support in Nunchuk Mode
Feature: Added ACC support in Nunchuk Mode
Feature: Added OSC support for IR
Feature: Added OSC support for Nunchuk Accelerometers
v0.4, 2009/03/16
Feature: Added (untested) Nunchuk support
v0.3, 2009/03/13
Bug: Fixed OS X compatibility (one MUST install lightblue using sudo!)
v0.2, 2009/03/12
Feature: Added OSC button support
Feature: Added (untested) Mac support
v0.1, 2009/03/06
Feature: Initial OSC functionality (ACC only)
Feature: Runs correctly on XP and Linux
ToDo:
* Cleanup the Code!
* If no Nunchuk is present, but activated, C/Z are always activated!
"""
import sys
if sys.platform == "darwin":
import lightblue
else:
import bluetooth
# The following enables correct termination on OS X and Win32
import signal
if sys.platform == "win32":
signal.signal(signal.SIGBREAK, signal.default_int_handler)
else:
signal.signal(15, signal.default_int_handler)
import math
import time
import liblo
import struct
# We control the Backend using command-line parameters; one can launch the backend
# directly or use the frontend to control the backend using OSC.
from optparse import OptionParser
parser = OptionParser()
parser.add_option("-v","--verbose",dest="verbose", action="store_true", default=False, help="output extra information")
parser.add_option("-a","--address", dest="address", help="WiiMote Address to connect to")
parser.add_option("-n","--number", dest="number", help="Number of Wiimote", default=0, type="int")
parser.add_option("-o","--osc", dest="osc_ip", help="OSC IP Address", default="127.0.0.1")
parser.add_option("-p","--port", dest="osc_port", help="OSC Port", default=5600, type="int")
parser.add_option("-s", "--scan", dest="scan", help="Scan for Devices", default=False, action="store_true")
(opt,argv) = parser.parse_args()
if not opt.address:
parser.error("No Address given, will now quit")
OSC_IP = opt.osc_ip.strip()
OSC_PORT = opt.osc_port
def i2bs(x):
'''Convert a (32-bit) int to a list of 4 byte values, e.g.
i2bs(0xdeadbeef) = [222,173,190,239]
12bs(0x4) = [0,0,0,4]'''
out=[]
while x or len(out) < 4:
out = [x & 0xff] + out
x = x >> 8
return out
buttonmap = {
'2': 0x0001,
'1': 0x0002,
'B': 0x0004,
'A': 0x0008,
'-': 0x0010,
'H': 0x0080,
'L': 0x0100,
'R': 0x0200,
'D': 0x0400,
'U': 0x0800,
'+': 0x1000,
}
nunchmap = {
'C': 0x0002,
'Z': 0x0001,
}
retromap2 = {
'U': 0x0001,
'L': 0x0002,
'ZR': 0x0004,
'X': 0x0008,
'A': 0x0010,
'Y': 0x0020,
'B': 0x0040,
'ZL': 0x0080,
}
retromap1 = {
'RT': 0x0002,
'+': 0x0004,
'H': 0x0008,
'-': 0x0010,
'LT': 0x0020,
'D': 0x0040,
'R': 0x0080,
}
statusmap = {
'Bat': 0x01,
'Ext': 0x02,
'Spk': 0x04,
'IR' : 0x08,
'L1' : 0x10,
'L2' : 0x20,
'L3' : 0x40,
'L4' : 0x80,
}
# BLUH. These should be less C-ish.
CMD_SET_REPORT = 0x52
RID_LEDS = 0x11
RID_MODE = 0x12
RID_IR_EN = 0x13
RID_SPK_EN = 0x14
RID_STATUS = 0x15
RID_WMEM = 0x16
RID_RMEM = 0x17
RID_SPK = 0x18
RID_SPK_MUTE = 0x19
RID_IR_EN2 = 0x1a
MODE_BASIC = 0x30
MODE_ACC = 0x31
MODE_IR = 0x33 # 0x32 Shouldn't this be 0x33???
MODE_FULL = 0x3e # Currently unused
MODE_EXT = 0x37 # We should use this mode to read from the Nunchuk
MODE_BB = 0x32 # This is used for the BalanceBoard
IR_MODE_OFF = 0
IR_MODE_STD = 1
IR_MODE_EXP = 3
IR_MODE_FULL = 5
FEATURE_DISABLE = 0x00
FEATURE_ENABLE = 0x04
# Max value for IR dots
DOT_MAX = 0x3ff
"""
The Wiimote class handles all communication and is responsible for
receiving and decrypting packets
"""
class Wiimote(object):
def __init__(self,addr,number=0):
self.target = liblo.Address(OSC_IP, OSC_PORT)
if sys.platform == 'darwin':
self.osx = True
else:
self.osx = False
self.connected=False
self.done=False
self.addr=addr
self.number=number
self.mode = 0
self.ledmask = 0
self.buttonmask = 0
self.nunchmask = 0
self.force = [0,0,0]
self.force_nunch= [0,0,0]
self.stick_nunch= [0,0]
self.force_zero = [0,0,0]
self.bb_force = [0,0,0,0]
self.force_1g = [0,0,0]
self.statusmask = 0
self.batterylevel = 0
self.retromask1 = 0
self.retromask2 = 0
self.ack = False
self.memresult = False
self.bundle = None
self.retro = False
self.force_1g_diff = [0,0,0] # Difference between zero and 1g
self.dots = [DOT_MAX,DOT_MAX,DOT_MAX,DOT_MAX,DOT_MAX,DOT_MAX,DOT_MAX,DOT_MAX]
if self.osx:
self.rx = lightblue.socket(lightblue.L2CAP)
self.cx = lightblue.socket(lightblue.L2CAP)
else:
self.rx = bluetooth.BluetoothSocket(bluetooth.L2CAP)
self.cx = bluetooth.BluetoothSocket(bluetooth.L2CAP)
def connect(self):
try:
self.rx.connect((self.addr,19))
self.cx.connect((self.addr,17))
self.connected = True
except:
self.connected = False
print "Connection Failed"
self.initialize()
def disconnect(self):
self.cx.close()
self.rx.close()
self.connected=False
def mainloop(self):
while not self.done:
self._getpacket()
def initialize(self):
self.enable_extension()
self.getstatus()
while self.batterylevel == 0: # batterylevel is used b/c self.statusmask=0 if no extension is inserted
self._getpacket()
if self.mode != MODE_BB:
self.setled(self.number)
def _handle_button_data(self,data): # Handle Button Data
buttonlist='+UDLRH-AB12'
newmask = (ord(data[2])<<8)+ord(data[3])
self.buttonmask = newmask
for c in buttonlist:
if self.buttonmask & buttonmap[c]:
self.bundle.add(liblo.Message("/wii/"+str(self.number)+"/button/"+c, 1))
def _handle_status_data(self, data): # Status Data handling
if len(data) != 8: return False
statuslist = ['Bat', 'Ext', 'Spk', 'IR', 'L1', 'L2', 'L3', 'L4']
newmask = ord(data[4])
if newmask & statusmap['Ext'] and not self.statusmask & statusmap['Ext']:
print "Extension inserted"
self.enable_extension() # We repeat the initalization sequence; this is a workaround for the Datel Wireless Nunchuk
self.enable_extension()
if not newmask & statusmap['Ext'] and self.statusmask & statusmap['Ext']:
print "Extension removed"
self.enable_extension()
self.statusmask = newmask
self.batterylevel = ord(data[7])
for c in statuslist:
if self.statusmask & statusmap[c]:
self.bundle.add(liblo.Message("/wii/"+str(self.number)+"/status/"+c, 1))
else:
self.bundle.add(liblo.Message("/wii/"+str(self.number)+"/status/"+c, 0))
print self.batterylevel
self.bundle.add(liblo.Message("/wii/"+str(self.number)+"/batterylevel", self.batterylevel))
def _handle_force_data(self,data):
if len(data) != 3: return False
self.force = [ord(d) for d in data]
self.bundle.add(liblo.Message("/wii/"+str(self.number)+"/acc", self.force[0], self.force[1], self.force[2]))
# self.force_calib = [self.force[0] - self.force_zero[0], self.force[1] - self.force_zero[1], self.force[2] - self.force_zero[2]]
return True
def _handle_bb_data(self, data): # Handle BalanceBoard Data
a,b,c,d,e,f,g,h = [ord(d) for d in data]
tr = (a << 8) + b
br = (c << 8) + d
tl = (e << 8) + f
bl = (g << 8) + h
self.bb_force = [tr,br,tl,bl]
- self.bundle.add(liblo.Message("/wii/"+str(self.number)+"/bb/force", self.bb_force[0], self.bb_force[1], self.bb_force[2]))
+ self.bundle.add(liblo.Message("/wii/"+str(self.number)+"/bb/force", self.bb_force[0], self.bb_force[1], self.bb_force[2]), self.bb_force[3])
def _handle_retro_data(self,data): # This handles the Classic Controller
retrolist2 = ['U', 'L', 'ZR', 'X', 'A', 'Y', 'B', 'ZL']
retrolist1 = ['RT', '+', 'H', '-', 'LT', 'D', 'R']
self.retromask2 = ord(data[5])
self.retromask1 = ord(data[4])
for c in retrolist1:
if not self.retromask1 & retromap1[c]:
self.bundle.add(liblo.Message("/wii/"+str(self.number)+"/cc/button"+c, 1))
for c in retrolist2:
if not self.retromask2 & retromap2[c]:
self.bundle.add(liblo.Message("/wii/"+str(self.number)+"/cc/button"+c, 1))
rx = ((ord(data[2]) & 0x0080) >> 7) + ((ord(data[1]) & 0x00C0) >> 5) + ((ord(data[0]) & 0x00C0) >> 3)
ry = ord(data[2]) & 0x1F
lx = ord(data[0]) & 0x3F
ly = ord(data[1]) & 0x3F
lt = ((ord(data[2]) & 0xE0) >> 5) + ((ord(data[1]) & 0x60) >> 2)
rt = ord(data[2]) & 0x1F
self.bundle.add(liblo.Message("/wii/"+str(self.number)+"/cc/rx", rx))
self.bundle.add(liblo.Message("/wii/"+str(self.number)+"/cc/ry", ry))
self.bundle.add(liblo.Message("/wii/"+str(self.number)+"/cc/lx", lx))
self.bundle.add(liblo.Message("/wii/"+str(self.number)+"/cc/ly", ly))
self.bundle.add(liblo.Message("/wii/"+str(self.number)+"/cc/rt", rt))
self.bundle.add(liblo.Message("/wii/"+str(self.number)+"/cc/lt", lt))
return True
def _handle_ext_data(self,data): # This gives only the raw Extension data
nunchlist = 'CZ'
acc = data[2:5]
stick = data[0:2]
self.nunchmask = ord(data[5]) & 0x03
self.force_nunch = [ord(d) for d in acc]
self.stick_nunch = [ord(d) for d in stick]
for c in nunchlist:
if not self.nunchmask & nunchmap[c]:
self.bundle.add(liblo.Message("/wii/"+str(self.number)+"/nunchuk/button/"+c, 1))
self.bundle.add(liblo.Message("/wii/"+str(self.number)+"/nunchuk/acc", self.force_nunch[0], self.force_nunch[1], self.force_nunch[2]))
self.bundle.add(liblo.Message("/wii/"+str(self.number)+"/nunchuk/stick", self.stick_nunch[0], self.stick_nunch[1]))
return True
def _handle_IR_data(self,data): # Handle Infrared Data
"""
There are two (in fact, three) possible infrared modes we need to distinguish
basic, extended and full, but only basic and extended are covered here
"""
if len(data) == 12: # The Mote can track 4 points
self.dots = self._handle_IR_ext(data[0:6])
self.dots.extend(self._handle_IR_ext(data[6:12]))
elif len(data) == 10:
self.dots = self._handle_IR_basic(data[0:5])
self.dots.extend(self._handle_IR_basic(data[5:10]))
else: return False
self.bundle.add(liblo.Message("/wii/"+str(self.number)+"/irdata", self.dots[0], self.dots[1], self.dots[2], self.dots[3], self.dots[4], self.dots[5], self.dots[6], self.dots[7]))
def _handle_IR_basic(self,data): # Handle Basic IR Data
if data == '\xff'*5: # No Dot found
dots = [DOT_MAX,DOT_MAX,DOT_MAX,DOT_MAX]
else:
a,b,c,d,e = [ord(d) for d in data]
x1 = a+((c & 0x30) << 4)
y1 = b+((c & 0xc0) << 2)
x2 = d+((c & 0x03) << 8)
y2 = e+((c & 0x0c) << 6)
dots = [x1,y1,x2,y2]
return dots
def _handle_IR_ext(self,data): # Handle extended IR data
if len(data) != 6: return False
if data ==' \xff'*6:
dots=[DOT_MAX,DOT_MAX,DOT_MAX,DOT_MAX]
else:
a,b,c,d,e,f = [ord(d) for d in data]
# processing dots:
# each tuple is 3 bytes in the form: x,y,extra
# extra contains 8 bits of extra data as follows: [yyxxssss]
# x and y are the high two bits for the full 10-bit x/y values.
# s is size data
x1=a+((c & 0x30) << 4)
y1=b+((c & 0xc0) << 2)
x2=d+((f & 0x30) << 4)
y2=e+((f & 0xc0) << 2)
dots=[x1,y1,x2,y2]
return dots
def _getpacket(self): # Receive packet and call handling routine
try:
data = self.rx.recv(1024)
self._handlepacket(data)
except:
print "Error receiving data"
self.done = True
def _handlepacket(self, data): # This handles packet data
""" Duplicate packets are handled recursively: The packet length
for the returned mode is checked against the appropriate length.
If that doesn't match, the remaining string is handled again.
If the remaining string is too short, Data is received again.
"""
self.bundle = liblo.Bundle()
if len(data) < 2:
data += self.rx.recv(1024)
if data.startswith('\xa1\x20'): # status
if len(data) < 8:
data += self.rx.recv(1024)
self._handle_status_data(data[0:8])
self._handle_button_data(data[0:4])
liblo.send(self.target, self.bundle)
if len(data) > 8:
self._handlepacket(data[8:])
elif data.startswith('\xa1\x21'): # Read Memory
if len(data) < 23:
data += self.rx.recv(1024)
self.memresult = data[0:23]
if len(data) > 23:
self._handlepacket(data[23:])
elif data.startswith('\xa1\x22'): # Acknowledge Command
if len(data) < 6:
self.rx.recv(1024)
self.ack = True
self._handle_button_data(data[0:4])
liblo.send(self.target, self.bundle)
if len(data) > 6:
self._handlepacket(data[6:])
elif data.startswith('\xa1\x30'): # button
if len(data) < 4:
data += self.rx.recv(1024)
self._handle_button_data(data[0:4])
liblo.send(self.target, self.bundle)
if len(data) > 4:
self._handlepacket(data[4:])
elif data.startswith('\xa1\x31'): # button + accelerometer
if len(data) < 7:
data += self.rx.recv(1024)
self._handle_button_data(data[0:4])
self._handle_force_data(data[4:7])
liblo.send(self.target, self.bundle)
if len(data) > 7:
self._handlepacket(data[7:])
elif data.startswith('\xa1\x32'): # button + 8 extensions bytes (BalanceBoard)
if len(data) < 12:
data += self.rx.recv(1024)
self._handle_button_data(data[0:4])
self._handle_bb_data(data[4:12])
liblo.send(self.target, self.bundle)
if len(data) > 12:
self._handlepacket(data[12:])
elif data.startswith('\xa1\x33'): # button + accel + IR
if len(data) < 19:
data += self.rx.recv(1024)
self._handle_button_data(data[0:4])
self._handle_force_data(data[4:7])
self._handle_IR_data(data[7:19])
liblo.send(self.target, self.bundle)
if len(data) > 19:
self._handlepacket(data[19:])
elif data.startswith('\xa1\x37'): # button + accel + IR + Nunchuk
if len(data) < 23:
data += self.rx.recv(1024)
self._handle_button_data(data[0:4])
self._handle_force_data(data[4:7])
self._handle_IR_data(data[7:17])
if not self.retro:
self._handle_ext_data(data[17:23])
else:
self._handle_retro_data(data[17:23])
liblo.send(self.target, self.bundle)
if len(data) > 23:
self._handlepacket(data[23:])
elif len(data) == 0: # Wiimote went away!
self.done = True
else:
print "Unknown packet len %i: 0x%s" % (len(data),data.encode("hex"))
def setled(self,num): # Enable a given LED
if num < 4:
self.ledmask = self.ledmask | (0x10 << num)
self._led_command()
def clearled(self,num): # Disable a given LED
if num < 4:
self.ledmask = self.ledmask & ~(0x10 << num)
self._led_command()
def getstatus(self): # Read the WiiMotes Status
self._send_command(CMD_SET_REPORT,RID_STATUS,[0])
def buttons_str(self): # Used only for Verbose Output
buttonlist='+UDLRH-AB12'
nunchlist = 'CZ'
out=''
for c in buttonlist:
if not self.buttonmask & buttonmap[c]:
c = '.'
out = out + c
for c in nunchlist:
if self.nunchmask & nunchmap[c]:
c = '.'
out = out + c
return out
def force_str(self): # Used only for Verbose Output
return "(% 4i,% 4i,% 4i)" % (self.force[0]-self.force_zero[0],
self.force[1]-self.force_zero[1],
self.force[2]-self.force_zero[2])
def dots_str(self): # Used only for Verbose Output
#print self.dots
return "((%4i,%4i),(%4i,%4i))" % (self.dots[0], self.dots[1], self.dots[2], self.dots[3])
def status_str(self): # Used only for Verbose Output
return "%i: %s force=%s dots=%s stick=%s nf=%s bb=%s" % \
(self.number,self.buttons_str(),self.force_str(),self.dots_str(),str(self.stick_nunch), str(self.force_nunch), str(self.bb_force))
def showstatus(self): # Verbose Output
sys.stdout.write(self.status_str() + "\r")
sys.stdout.flush()
def setmode(self,mode): # Send desired Mode to Mote
self.mode = mode
# XXX wiimotulator.py has flags for setting 0x01 in the first byte for
# 'rmbl' and 0x04 for 'cont'. Both of these are always off.
# No idea why.
self._send_command(CMD_SET_REPORT,RID_MODE,[0,mode])
def enable_force(self): # Enable Accelerometer and read Calibration Data
#self.setmode(self.mode | MODE_ACC)
self.get_force_calibration()
def enable_IR(self): # Enable the Infrared Device
if self.mode != MODE_EXT and self.mode != MODE_BB: # TODO: MODE_BB is not used here, right?
self.setmode(self.mode | MODE_IR)
self._send_command(CMD_SET_REPORT,RID_IR_EN,[FEATURE_ENABLE])
self._send_command(CMD_SET_REPORT,RID_IR_EN2,[FEATURE_ENABLE])
# Enable IR device
self._write_mem(0x04b00030,[0x01])
# Set sensitivity constants
self._write_mem(0x04b00030,[0x08])
self._write_mem(0x04b00006,[0x90]) # Write Block1 in 2 individual bytes; Suggested by Marcan
self._write_mem(0x04b00008,[0xc0]) # Write Block1 second half; Suggested by Marcan
self._write_mem(0x04b0001a,[0x40]) # Write Block2 as suggested by Marcan
if self.mode != 0x37: # This is Extended Mode
self._write_mem(0x04b00033,[0x33]) # Write Mode Number; Why is it 0x33??
else:
self._write_mem(0x04b00033,[0x31]) # Basic Mode
# Enable IR data output
self._write_mem(0x04b00030,[8])
def enable_extension(self): # Enable Nunchuk; This procedure should work on Original and 3rd party Nunchuks!
self._write_mem(0x04a400f0,[0x55])
self._write_mem(0x04a400fb,[0x00])
ext_data = self._read_mem(0x04a400fa,6)
if ext_data[2:6] == "\xa4\x20\x00\x00":
self._write_mem(0x04a40040,[0x05, 0xa0, 0xb2, 0x1d, 0x98, 0xac]) # Set up 16 byte encryption key
self._write_mem(0x04a40046,[0x8b, 0x26, 0xc1, 0xd9, 0x39, 0x64]) # It is necessary for 3rd party Nunchuk
self._write_mem(0x04a4004c,[0x52, 0x0c, 0x73, 0x05])
self.setmode(MODE_EXT) # The mode MUST be set right now, not beforehand (3rd party Nunchuk!)
print "Nunchuk connected, using 10 Byte IR"
self.enable_IR() # IR needs to be re-enabled to work correctly
elif ext_data[2:6] == "\xa4\x20\x01\x01":
self.setmode(MODE_EXT)
self.retro = True
print "Retro Controller connected, using 10 Byte IR"
self.enable_IR() # Re-enable IR
elif ext_data[2:6] == "\xa4\x20\x04\x02":
self.setmode(MODE_BB)
self.setled(0)
print "BalanceBoard connected"
self.get_bb_calibration()
else:
print "Extension not connected, using 12 Byte IR"
self.retro = False
self.setmode(MODE_IR)
self.enable_force()
self.enable_IR()
def get_bb_calibration(self): # This is not a very nice function...
bundle = liblo.Bundle()
data1 = self._read_mem(0x04a40024, 12) # Get the first half of calibration data
data2 = self._read_mem(0x04a40030, 12)
if data1:
a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p = [ord(d) for d in data1]
tr0 = (a << 8) + b
br0 = (c << 8) + d
tl0 = (e << 8) + f
bl0 = (g << 8) + h
tr17 = (i << 8) + j
br17 = (k << 8) + l
if data2:
a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p = [ord(d) for d in data2]
tl17 = (a << 8) + b
bl17 = (c << 8) + d
tr34 = (e << 8) + f
br34 = (g << 8) + h
tl34 = (i << 8) + j
bl34 = (k << 8) + l
if data1 and data2:
self.bb_calib = [tr0,br0,tl0,bl0,tr17,br17,tl17,bl17,tr34,br34,tl34,bl34]
#print self.bb_calib
bundle.add(liblo.Message("/wii/"+str(self.number)+"/bb/calib", tr0, br0, tl0, bl0, tr17, br17, tl17, bl17, tr34, br34, tl34, bl34))
liblo.send(self.target, bundle)
def get_force_calibration(self): # Retrieve Acc calibration Data
# FIXME: calib data is never sent via OSC
data = None
n = 0
while data == None and n<32:
data = self._read_mem(0x16,10)
n = n+1
if data:
data=[ord(b) for b in data]
self.force_zero = data[0:3]
self.force_1g = data[4:7]
# XXX currently we don't know what data[3], data[7], or data[8:9] are
# Calculate the difference between zero and 1g for each axis
for b in range(0,3):
self.force_1g_diff[b] = self.force_1g[b] - self.force_zero[b]
def _led_command(self): # Send LED commands to the WiiMote
self._send_command(CMD_SET_REPORT,RID_LEDS,[self.ledmask])
def _waitforok(self): # Wait for OK/ACK packet
n = 0
while not self.ack and n<32:
self._getpacket()
n = n + 1
self.ack = False
def _read_mem(self,offset,size): # Read a given memory address
if size >= 16:
print "ERROR: _read_mem can't handle size > 15 yet"
return None
# RMEM command wants: [offset,size]
self._send_command(CMD_SET_REPORT,RID_RMEM,i2bs(offset)+[0,size])
n = 0
while not self.memresult and n<32:
self._getpacket()
n = n + 1
data = self.memresult
self.memresult = False
if data:
# TODO check error flag, continuation, etc
return data[7:]
else:
return None
def _write_mem(self,offset,data): # Write to memory
# WMEM command wants: [offset,size,data]
# offset = 32-bit, bigendian. data is 0-padded to 16 bytes.
size = len(data)
if size > 16: return False # Too much data!
if size < 16: data = data + [0]*(16-size)
self._send_command(CMD_SET_REPORT,RID_WMEM,i2bs(offset)+[size]+data)
self._waitforok()
def _send_command(self,cmd,report,data): # Send a specific command
self.cx.send(chr(cmd) + chr(report) + "".join([chr(d) for d in data]))
if __name__ == '__main__':
if opt.scan:
target = liblo.Address(OSC_IP, OSC_PORT)
print 'Discovering Bluetooth Devices, please wait...'
if sys.platform == 'darwin':
devicelist = lightblue.finddevices()
else:
devicelist = bluetooth.discover_devices(lookup_names = True)
devicefound = False
if len(devicelist) > 0: # If devices are found, cycle through the list and check the names
devicelist = list(set(devicelist)) # This removes duplicates on Win32; really nice function!
for ii in range(0,len(devicelist)):
if devicelist[ii][1] == 'Nintendo RVL-CNT-01' or devicelist[ii][1] == 'Nintendo RVL-WBC-01':
liblo.send(target, liblo.Message('/wii/devices', devicelist[ii][0], devicelist[ii][1].encode()))
print 'Found: ' + devicelist[ii][0] + ' ' + devicelist[ii][1]
devicefound = True
if devicefound:
print "Done discovering"
print "Continue with connecting"
devicefound = False
else:
liblo.send(target, liblo.Message('/wii/devices', '0', 'nodev'))
print "No device was found. - Try again"
else: # No scan was requested, just start to capture packets
print "Connecting - press 1+2 on your Wiimote."
w = Wiimote(opt.address.strip(), opt.number)
w.connect()
if w.connected: # Only enable and initalize the controller, if connection was successful
# w.getstatus() # Before we start capturing, we retrieve status information
try:
last=time.time()
while not w.done:
w._getpacket()
if opt.verbose:
w.showstatus()
finally:
w.disconnect()
diff --git a/wiipy_frontend.py b/wiipy_frontend.py
--- a/wiipy_frontend.py
+++ b/wiipy_frontend.py
@@ -1,247 +1,246 @@
#!/usr/bin/env python
"""
Frontend for wiipy_backend.py
(c) 2009 Andreas Boehler, andreas.boehler@fh-linz.at, http://klasseonline.dyndns.org
-(c) 2009 Patrick Huebner, patrick.huebner@fh-linz.at
-Requirement:
+Requirements:
* pybluez (Windows/Linux), lightblue (OS X)
* pyliblo (all platforms)
Notes:
* wiimote_backend.py and wiimote_frontend.py need to be in the same dir!
* The current dir must be set to wiimote_[frontend|backend].py
* The Microsoft-BT Stack is NOT supported! Tested: Widcomm under XP
* The path in OS X must not contain spaces!
v1.8, 2009/05/22
Feature: Migrated to pyliblo from simpleosc which is a major speed improvement
v1.7, 2009/05/11
Feature: Removed setup sequence, initialization is now completely automatic; only left: debug
v1.6.3, 2009/05/08
Bugfix: Forgot int(msg) in disconnect-code
v1.6.2, 2009/05/06
Bugfix: Path to python.exe is now taken from sys.executable (should work on all platforms)
v1,6.1, 2009/05/05
Feature: Added debugging/verbose parameter support
v1.6, 2009/04/28
Feature: All OSC IPs and Ports can be freely configured now!
v1.5.1, 2009/04/27
Feature: Added ping command, status still to be done
v1.5, 2009/04/26
Bugfix: Correctly Terminate the process on MacOS X.
v1.4, 2009/04/24
Bugfix: Correctly Terminate the process on Win32. This took me about 6h to figure out!
v1.3.1, 2009/04/23
Bugfix: Changed from SIGKILL to SIGTERM for Unix kill method
v1.3, 2009/04/23
Feature: Added Device Discovery Routine
Bugfix: Fixed Python 2.6 compatibility
v1.2, 2009/04/23
Bugfix: Frontend used 100% CPU because of infinite loop
v1.1, 2009/04/20
Bugfix: Fixed OS X Compatibility
v1.0, 2009/04/09
Bugfix: Fixed Windows Compatibility (tested on Vista)
Note: Path to python.exe is hardcoded for now!
v0.2, 2009/04/08
Feature: Added OSC support
v0.1, 2009/04/07
Feature: Initial Version w/o OSC support
OSC Format description:
Address: "/wiipy"
Format: [Type, Number, Options]
Tuple: ["setup", 0, "debug", 1]
Tuple: ["connect", 0, "00:4f:4e:13:56:40"]
Tuple: ["disconnect", 0]
Tuple: ["shutdown"] stops alls connections and quits the wiipy_frontend.py
Tuple: ["discover"] starts Bluetooth-Discovery and sends the results via OSC
Tuple: ["ping"] sends a simple "OK" via OSC
Tuple: ["status"] reports connections via OSC
Tuple: ["config", "ip", "127.0.0.1", "port", 5600] configure osc IP and Port to connect to
"""
import os
import subprocess
import sys
import time as timer
from liblo import *
from optparse import OptionParser
parser = OptionParser()
parser.add_option("-i","--ip", dest="ip", help="OSC Server Address", default="127.0.0.1")
parser.add_option("-p","--port", dest="port", help="OSC Server Port", default=5601, type="int")
parser.add_option("-d", "--debug", dest="debug", help="Enable debugging", default=False, action="store_true")
(opt,argv) = parser.parse_args()
OSC_IP = opt.ip.strip()
OSC_PORT = opt.port
class spawn_wiimote(object):
"""
This class spawns another process using various cmd-line arguments.
It is used to run the backend with the necessary arguments, because
threads are not safe using lightblue
"""
def __init__(self, filename, args):
self.filename = filename
self.args = args
def spawn(self):
# We use the new subprocess class to spawn the command
if sys.platform == "win32":
self.process = subprocess.Popen([self.filename] + self.args, creationflags=512)
else:
self.process = subprocess.Popen([self.filename] + self.args)
self.pid = self.process.pid
if opt.debug:
print self.args
print "Spawned wiipy_backend.py with PID: " + str(self.pid)
def kill(self):
# The kill method depends on the platform, the win32 code took me forever to figure out!
if not self.process.poll():
if opt.debug:
print "Killing Process..."
if sys.platform == 'win32':
import ctypes
ctypes.windll.kernel32.GenerateConsoleCtrlEvent(1, self.pid)
else: # This is the Unix Method of killing a PID
os.kill(self.pid, 15)
def status(self):
if not self.process.poll():
return True
else:
return False
class oscServer(ServerThread):
"""
This class defines an OSC Server Process, parses the options sent and
spawns the backend
"""
def __init__(self, ip='127.0.0.1', port=5601):
self.ip = ip
self.port = port
ServerThread.__init__(self, self.port)
self.osc_ip = '127.0.0.1'
self.osc_port = 5600
self.debug = [0, 0, 0, 0]
self.address = [0, 0, 0, 0]
self.wiimote = [None, None, None, None]
self.connected = True
if sys.platform == 'darwin':
self.osx = True
else:
self.osx = False
print "Listening on " + self.ip + ':' + str(self.port)
@make_method('/wiipy', None)
def wiipy_callback(self, path, args): #Yeah, this is ugly, I know...
if args[0] == "setup":
print "Setting up: " + str(int(args[1]))
nr = int(args[1])
if args[2] == "debug":
self.debug[nr] = int(args[3])
if opt.debug:
print self.debug
elif args[0] == "connect":
address = args[2]
nr = int(args[1])
print "Connecting: " + str(nr) + " to " + address
args = ["-a " + address, "-n " + str(nr), "-o " + self.osc_ip, "-p " + str(self.osc_port)]
if opt.debug:
print args
if self.debug[nr] == 1:
args.append("-v")
if self.wiimote[nr] == None:
args.insert(0, 'wiipy_backend.py')
self.wiimote[nr] = spawn_wiimote(sys.executable, args)
self.wiimote[nr].spawn()
elif args[0] == "disconnect":
nr = int(args[1])
print "Disconnecting from: " + str(nr)
if self.wiimote[nr] != None:
self.wiimote[nr].kill()
self.wiimote[nr] = None
elif args[0] == "shutdown":
self.stopOsc()
elif args[0] == "discover":
self.discover_devices()
elif args[0] == "ping":
if opt.debug:
print "Ping successful"
send(Address(self.osc_ip, self.osc_port), Message('/wii/ping', 'OK'))
elif args[0] == "status":
self.get_status()
elif args[0] == "config":
for ii in range(0,len(args)-1):
if args[ii+1] == "ip":
self.osc_ip = args[ii+2]
elif args[ii+1] == "port":
self.osc_port = int(args[ii+2])
print 'Now sending to: ' + self.osc_ip + ':' + str(self.osc_port)
def stopOsc(self):
print "Disconnecting all Wiimotes"
for ii in range(0,4):
if self.wiimote[ii] != None:
self.wiimote[ii].kill()
print "Stop OSC"
self.connected = False
def get_status(self): # Get the current Status of connected WiiMotes
print "Status"
statslist = [0, 0, 0, 0]
for ii in range(0,4):
if self.wiimote[ii] != None:
if self.wiimote[ii].status:
statslist[ii] = 1
if opt.debug:
print statslist
send(Address(self.osc_ip, self.osc_port), Message('/wii/status', statslist[0], statslist[1], statslist[2], statslist[3]))
def discover_devices(self): #The discoverer is in reality the backend, so we spawn it
args = ["wiipy_backend.py", "-a 00:00:00:00:00:00", "-s", "-o " + self.osc_ip, "-p " + str(self.osc_port)]
if opt.debug:
print args
sp = spawn_wiimote(sys.executable, args)
sp.spawn()
if __name__ == '__main__':
"""
We only need a short OSC Server and need to handle termination correctly,
that is by stopping the server and disconnecting all clients.
Note that the OSC Server is, at least under Linux, only stopped upon reception
of the next message!
"""
s = oscServer(OSC_IP, OSC_PORT)
s.start()
print "wiipy_frontend.py initialized, waiting for commands"
try:
while s.connected:
timer.sleep(1)
except:
print "Exit"
s.stopOsc()

File Metadata

Mime Type
text/x-diff
Expires
Fri, Jan 24, 4:14 AM (1 d, 17 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
533777
Default Alt Text
(37 KB)

Event Timeline