Python driver

From WiiLi

Jump to: navigation, search

A driver written in python for OSX.

I have long searched for a Wiiremote driver for OSX, accessable by python. All existing drivers and tools either do not support osx or don't have access to the nunchuck. So i decided to write one myself.

This driver was created for my bachelor project called riimod, a video of my installation can be found on: [1]

Contents

[edit] Requirements

lightblue

pygame (for the visual demo of the output)

[edit] Todo's

Testing...

Improve the demo

Port to other systems (windows/linux) (partially done, not much tested)

[edit] Sources of information

http://www.wiili.org/

http://wiibrew.org/

WMD driver

pywiimote

- (http://code.google.com/p/pywiimote/)

wiiwhiteboard

- (https://sourceforge.net/projects/wiiwhiteboard/)

- (especially the linux implementation)

Thanks to all of the ppl's who have worked on the above sources.

[edit] Hints

I have installed Osculator, which contains a kernel extension, which afaik helps connecting the wiimote (without this extension the connection often fails, you would have to disable/enable bluetooth to make it work again)

[edit] Sourcecode

# changelog
# 13.3.2008:
# - using a dialogue to select the wiimote
# 4.6.2008:
# - added some more info
# - new version upload to wiili.org

# ---
# by Reto Spoerri
# rspoerri (@) nouser.org (http://www.nouser.org)
# A Python Wii-Driver, supporting the Nunchuck for OSX
# ---

# ---
# License:
# Free to use and modify (as long as the credits/license remain) in non-commercial products.
# Contact the autor if planned to use/used in a commercial project.
# ---

# ---
# requirements:
# - lightblue
# ---

# ---
# implemented functions:
# - wiimote:
#   - rumble
#   - buttons
#   - ir
#   - multiple ir sensitivity modes
#   - gravity
#   - 12 of the 14 types of known package formats (not the interleaved ones)
#   - gives infos about the unknown packages
# - nunchuck:
#   - can be plugged/unplugged during runtime (not that obvious that this works :)
#   - gravity
#   - buttons
#   - joystick
# ---

# ---
# known limitations:
# SCRIPT:
#   - this script should be called 100times per second
#     - only the last received package data will be stored
#     - all other packages will be handled, but unavailable as there is not history in data
#   - if it's not called 100times per second it will usually work fine as well
#     you will just have missed some packages data
#   - not sure if all calibration data from the wiimote are handled correctly
# OPERATING SYSTEMS:
# - WINDOWS:
#   - you will need a bluetooth stack from a different manufacturer (bluetooth stick)
#     - WIDCOMM should work (lightblue only supports widcom afaik)
#   - tested badely
# - OSX:
#   - check the hardware limitations if using multiple wiimotes
#   - tested quite well
# - Linux:
#   - tested a little bit
# - there are some differences between the implementations:
#   - only osx gives a dialogue if not mac address is specified for your wiimote
#     (also this feature was not checked since a while)
# HARDWARE:
#   - If using multiple wiimotes with this library check your bluetooth hardware
#     - it may result in lags...
#       - i have made bad exeperience with a usb1.0 bluetooth sticks
#       - i have made good experience with macbook pro / mac pro internal bluetooth hardware
# ---
 
# ---
# example programm:
# ---
# requirements:
# - pygame (for the demo)
# ---
# usage:
# - change the wiimote mac address in the code (look for the string at the end of this file)
#   - to get your wiimotes mac address you can use lightblue   (lightblue.finddevices())
# - run it by calling this script
# edit the ir-sensitivity:
# - '+' and '-' buttons on the wiimote
# ---


import os
import sys
import time
import atexit
import socket

from collections import deque

SIGNAL_RUMBLE = 0x01
SIGNAL_LED1   = 0x10
SIGNAL_LED2   = 0x20
SIGNAL_LED3   = 0x40
SIGNAL_LED4   = 0x80

# Wiimote output commands
OutputReportNone            = 0x00
OutputReportLEDs            = 0x11
OutputReportType            = 0x12
OutputReportIR              = 0x13
OutputReportSpeaker         = 0x14
OutputReportStatus          = 0x15
OutputReportWriteMemory     = 0x16
OutputReportReadMemory      = 0x17
OutputReportSpeakerData     = 0x18
OutputReportSpeakerMute     = 0x19
OutputReportIR2             = 0x1a
# Wiimote registers
REGISTER_IR                     = 0x04b00030
REGISTER_IR_SENSITIVITY_1       = 0x04b00000
REGISTER_IR_SENSITIVITY_2       = 0x04b0001a
REGISTER_IR_MODE                = 0x04b00033
REGISTER_EXTENSION_INIT         = 0x04a40040
REGISTER_EXTENSION_TYPE         = 0x04a400fe
REGISTER_EXTENSION_CALIBRATION  = 0x04a40020
# Extension Types
ExtensionTypeNone              = 0x00
ExtensionTypeNunchuk           = 0xfe
ExtensionTypeClassicController = 0xfd
# IR modes
IrModeOff                      = 0x00
IrModeBasic                    = 0x01 # 10 bytes
IrModeExtended                 = 0x03 # 12 bytes
IrModeFull                     = 0x05 # 16 bytes * 2 (format unknown)

SIGNAL_IR_SENSITIVITY = {
  # [address, data, dataLength] (if possible dont use heading 0x00's (it might not work), 0x12 is ok)
  'lvl1' : [ [REGISTER_IR_SENSITIVITY_1, 0x0200007101006400fe, 9], [REGISTER_IR_SENSITIVITY_2, 0xfd05, 2] ] # lvl 1
, 'lvl2' : [ [REGISTER_IR_SENSITIVITY_1, 0x0200007101009600b4, 9], [REGISTER_IR_SENSITIVITY_2, 0xb304, 2] ] # lvl 2
, 'lvl3' : [ [REGISTER_IR_SENSITIVITY_1, 0x020000710100aa0064, 9], [REGISTER_IR_SENSITIVITY_2, 0x6303, 2] ] # lvl 3
, 'lvl4' : [ [REGISTER_IR_SENSITIVITY_1, 0x020000710100c80036, 9], [REGISTER_IR_SENSITIVITY_2, 0x3503, 2] ] # lvl 4
, 'lvl5' : [ [REGISTER_IR_SENSITIVITY_1, 0x020000710100720020, 9], [REGISTER_IR_SENSITIVITY_2, 0x1f03, 2] ] # lvl 5
, 'some1': [ [REGISTER_IR_SENSITIVITY_1, 0x020000710100aa0064, 9], [REGISTER_IR_SENSITIVITY_2, 0x6303, 2] ] # some value...
, 'some2': [ [0x04b00006, 0x90, 1], [0x04b00008, 0x41, 1], [REGISTER_IR_SENSITIVITY_2, 0x4000, 2] ]
, 'max'  : [ [0x04b00006, 0x90, 1], [0x04b00008, 0x64, 1], [REGISTER_IR_SENSITIVITY_2, 0x6303, 2] ] # (max sensitivity)
# there are just some tests, will not work
#, 'my1'  : [ [REGISTER_IR_SENSITIVITY_1, 0x020000710100720001, 9], [REGISTER_IR_SENSITIVITY_2, 0x0000, 2] ] # lvl 5
#, 'my2'  : [ [REGISTER_IR_SENSITIVITY_1, 0x020000710100720005, 9], [REGISTER_IR_SENSITIVITY_2, 0x0400, 2] ] # lvl 5
}  # is max sensitivity

SIGNAL_IR_SENSITIVITY_SORTED_LIST = SIGNAL_IR_SENSITIVITY.keys() #['some1', 'lvl1', 'lvl2', 'lvl3', 'lvl4', 'lvl5', 'some2', 'max', 'my1', 'my2']

#SIGNAL_IR_SENSITIVITY_NAME 

class WIIMOTESTATE:
  def __init__( self ):
    self.SEND_ACCEL = False
    self.SEND_IR = False
    self.SEND_CONTINUOUS = False
    self.SEND_EXTENSION = False
    self.TRANSMISSION_ENABLED = True
    self.BATTERY = 0
    self.LED1 = self.LED2 = self.LED3 = self.LED4 = False
    self.RUMBLE = False
    self.EXTENSIONTYPE = None
    self.ExtensionClass = None
    self.KEEP_HISTORY = False
    self.IR_SENSITIVITY = 'max'
    
    # Accel data
    self.AccelCalibrationInfoX0 = self.AccelCalibrationInfoXG = 0.0
    self.AccelCalibrationInfoY0 = self.AccelCalibrationInfoYG = 0.0
    self.AccelCalibrationInfoZ0 = self.AccelCalibrationInfoZG = 0.0
    self.AccelRawX = self.AccelRawY = self.AccelRawZ = 0.0
    self.AccelX = self.AccelY = self.AccelZ = 0.0
    self.AccelHistoryLen = 50
    
    self.AccelHistoryX = list()
    self.AccelHistoryY = list()
    self.AccelHistoryZ = list()
    
    # IR data
    self.IrRawX1 = self.IrRawX2 = self.IrRawX3 = self.IrRawX4 = 0
    self.IrRawY1 = self.IrRawY2 = self.IrRawY3 = self.IrRawY4 = 0
    self.IrFound1 = self.IrFound2 = self.IrFound3 = self.IrFound4 = 0
    self.IrSize1 = self.IrSize2 = self.IrSize3 = self.IrSize4 = 0
    self.IrX1, self.IrX2, self.IrX3, self.IrX4 = 0.0,0.0,0.0,0.0
    self.IrY1, self.IrY2, self.IrY3, self.IrY4 = 0.0,0.0,0.0,0.0
    self.IrHistoryX1 = list()
    self.IrHistoryY1 = list()
    self.IrHistoryX2 = list()
    self.IrHistoryY2 = list()
    self.IrHistoryX3 = list()
    self.IrHistoryY3 = list()
    self.IrHistoryX4 = list()
    self.IrHistoryY4 = list()
    
    # Button data
    self.ButtonA = self.ButtonB = False
    self.ButtonUp = self.ButtonDown = self.ButtonLeft = self.ButtonRight = False
    self.ButtonHome = False
    self.ButtonMinus = self.ButtonPlus = False
    self.ButtonOne = self.ButtonTwo = False
    self.ButtonState = ''
  
  def getAttr( self, valuename ):
    return getattr( self, valuename )
  
  def IrUpdated( self ):
    pass
  
  def accelUpdated( self ):
    self.AccelHistoryX.append( self.AccelX )
    self.AccelHistoryX.pop(0)
    #del( self.AccelHistoryX[:max(0, len(self.AccelHistoryX)-self.AccelHistoryLen)] )
    self.AccelHistoryY.append( self.AccelY )
    self.AccelHistoryY.pop(0)
    #del( self.AccelHistoryY[:max(0, len(self.AccelHistoryY)-self.AccelHistoryLen)] )
    self.AccelHistoryZ.append( self.AccelZ )
    self.AccelHistoryZ.pop(0)
    #del( self.AccelHistoryZ[:max(0, len(self.AccelHistoryZ)-self.AccelHistoryLen)] )
  
  def getLedSignal( self ):
    #  (52) 11 XX
    signal = 0
    if self.LED1: signal += SIGNAL_LED1
    if self.LED2: signal += SIGNAL_LED2
    if self.LED3: signal += SIGNAL_LED3
    if self.LED4: signal += SIGNAL_LED4
    if self.RUMBLE: signal += SIGNAL_RUMBLE
    return signal
  
  def getSensitivitySignals( self ):
    #if self.IR_SENSITIVITY in SIGNAL_IR_SENSITIVITY.keys():
    return SIGNAL_IR_SENSITIVITY[self.IR_SENSITIVITY]
    #else:
    #  id = SIGNAL_IR_SENSITIVITY.keys()[self.IR_SENSITIVITY]
    #  return SIGNAL_IR_SENSITIVITY[id]
  
  def getRumbleSignal( self ):
    signal = 0
    if self.RUMBLE: signal += SIGNAL_RUMBLE
    return signal
  
  def getSendSignal( self ):
    # ba       0000001
    # bx       0000010
    # bai      0000011
    # bx       0000100
    # bax      0000101
    
    # (52) 12 00 XX
    signal = 0x30
    if self.SEND_ACCEL:
      signal += 0x01
    if self.SEND_IR:
      signal += 0x02
    if self.SEND_EXTENSION:
      signal += 0x04
    return signal
  
  def getTransmissionSignal( self ):
    # (52) 13 XX
    signal = 0
    if self.SEND_CONTINUOUS:
      signal += 0x04
    if self.RUMBLE:
      signal += 0x01
    return signal
  
  def getIrMode( self ):
    print "WIIMOTESTATE.getIrMode: IR %s, ACCEL %s, EXTENSION %s" % (['OFF','ON'][self.SEND_IR]
      , ['OFF','ON'][self.SEND_ACCEL]
      , ['OFF','ON'][self.SEND_EXTENSION]),
    if self.SEND_IR:
      if self.SEND_ACCEL:
        if self.SEND_EXTENSION:
          print "basic"
          return IrModeBasic # must be this
        else:
          print "extended"
          return IrModeExtended # must be this
      else:
        print "extended"
        return IrModeExtended
    else:
      print "off"
      return IrModeOff
  
  def __repr__debug__( self ):
    txt = "B:%2d LED: 1:%s 2:%s 3:%s 4:%s R: %s EXT:%s" %  (
      self.BATTERY
    , [' ','*'][bool(self.LED1)]
    , [' ','*'][bool(self.LED2)]
    , [' ','*'][bool(self.LED3)]
    , [' ','*'][bool(self.LED4)]
    , [' ','*'][bool(self.RUMBLE)]
    , str(self.EXTENSIONTYPE) )
    if self.SEND_ACCEL:
      txt += "AccelCalib: X0: %3d Y0: %3d Z0: %3d  XG: %3d YG: %3d ZG: %3d" % (
        self.AccelCalibrationInfoX0
      , self.AccelCalibrationInfoY0
      , self.AccelCalibrationInfoZ0
      , self.AccelCalibrationInfoXG
      , self.AccelCalibrationInfoYG
      , self.AccelCalibrationInfoZG )
      txt += "AccelRaw: %4d %4d %4d" % ( self.RawX, self.RawY, self.RawZ )
    if self.SEND_IR:
      txt += "IR: 1: %s %4d %4d %2d 2: %s %4d %4d %2d 3: %s %4d %4d %2d 4: %s %4d %4d %2d" % (
        [' ','*'][self.IrFound1], self.IrRawX1, self.IrRawY1, self.IrSize1
      , [' ','*'][self.IrFound2], self.IrRawX2, self.IrRawY2, self.IrSize2
      , [' ','*'][self.IrFound3], self.IrRawX3, self.IrRawY3, self.IrSize3
      , [' ','*'][self.IrFound4], self.IrRawX4, self.IrRawY4, self.IrSize4 )
    txt  = "Button: %s:%s %s:%s " % ("A", [' ','*'][self.ButtonA],     "B", [' ','*'][self.ButtonB])
    txt += "%s:%s %s:%s " % ("^", [' ','*'][self.ButtonUp],    "v", [' ','*'][self.ButtonDown]) 
    txt += "%s:%s %s:%s " % ("<", [' ','*'][self.ButtonLeft],  ">", [' ','*'][self.ButtonRight])
    txt += "%s:%s %s:%s " % ("-", [' ','*'][self.ButtonMinus], "+", [' ','*'][self.ButtonPlus])
    txt += "%s:%s %s:%s " % ("1", [' ','*'][self.ButtonOne],   "2", [' ','*'][self.ButtonTwo])
    txt += "%s:%s" % ("Home", [' ','*'][self.ButtonHome])
    return txt
  
  def __repr__( self ):
    txt = "B:%3d IR: 1:%s 2:%s 3:%s 4:%s R: %s EXT:%s " %  ( self.BATTERY
                                                    , [' ','*'][bool(self.LED1)]
                                                    , [' ','*'][bool(self.LED2)]
                                                    , [' ','*'][bool(self.LED3)]
                                                    , [' ','*'][bool(self.LED4)]
                                                    , [' ','*'][bool(self.RUMBLE)]
                                                    , str(self.EXTENSIONTYPE) )
    txt  = "Button: %s:%s %s:%s " % ("A", [' ','*'][self.ButtonA], "B", [' ','*'][self.ButtonB])
    txt += "%s:%s %s:%s " % ("^", [' ','*'][self.ButtonUp]       , "v", [' ','*'][self.ButtonDown]) 
    txt += "%s:%s %s:%s " % ("<", [' ','*'][self.ButtonLeft]     , ">", [' ','*'][self.ButtonRight])
    txt += "%s:%s %s:%s " % ("-", [' ','*'][self.ButtonMinus]    , "+", [' ','*'][self.ButtonPlus])
    txt += "%s:%s %s:%s " % ("1", [' ','*'][self.ButtonOne]      , "2", [' ','*'][self.ButtonTwo])
    txt += "%s:%s" % ("Home", [' ','*'][self.ButtonHome])
    if self.SEND_ACCEL:
      txt += "Accel: %2.2f %2.2f %2.2f " % (self.AccelX, self.AccelY, self.AccelZ)
    if self.SEND_IR:
      txt += "Ir: 1: %s %4d %4d %2d 2: %s %4d %4d %2d 3: %s %4d %4d %2d 4: %s %4d %4d %2d" % \
        ( [' ','*'][self.IrFound1], self.IrRawX1, self.IrRawY1, self.IrSize1
        , [' ','*'][self.IrFound2], self.IrRawX2, self.IrRawY2, self.IrSize2
        , [' ','*'][self.IrFound3], self.IrRawX3, self.IrRawY3, self.IrSize3
        , [' ','*'][self.IrFound4], self.IrRawX4, self.IrRawY4, self.IrSize4 )
    return txt
  
  def __str__( self ):
    return self.__repr__()

class WIIMOTEDUMMYEXTENSIONSTATE:
  def __repr__( self ):
    return "WIIMOTEDUMMYEXTENSIONSTATE"
  
  def __str__( self ):
    return self.__repr__()

class WIIMOTENUNCHUKEXTENSIONSTATE:
  AccelX = AccelRawX = AccelCalibrationX0 = AccelCalibrationXG = 0.0
  AccelY = AccelRawY = AccelCalibrationY0 = AccelCalibrationYG = 0.0
  AccelZ = AccelRawZ = AccelCalibrationZ0 = AccelCalibrationZG = 0.0
  MaxX = MidX = MinX = RawX = X = 0.0
  MaxY = MidY = MinY = RawY = Y = 0.0
  ButtonC = ButtonZ = False
  
  def __repr__debug__( self ):
    txt =  "NUNCHUK: X: %2.2f %3d %3d %3d Y: %2.2f %3d %3d %3d Z: %2.2f %3d %3d %3d" % (
      self.AccelX, self.AccelRawX, self.AccelCalibrationX0, self.AccelCalibrationXG
    , self.AccelY, self.AccelRawY, self.AccelCalibrationY0, self.AccelCalibrationZ0
    , self.AccelZ, self.AccelRawZ, self.AccelCalibrationYG, self.AccelCalibrationZG
    )
    txt += "X: %2.2f %3d %3d %3d %3d Y: %2.2f %3d %3d %3d %3d" % (
      self.X, self.RawX, self.MinX, self.MidX, self.MaxX
    , self.Y, self.RawY, self.MinY, self.MidY, self.MaxY
    )
    return txt
  
  def __repr__( self ):
    txt =  "NUNCHUK: X: %3d Y: %3d Z: %3d " % (
      self.AccelX, self.AccelY, self.AccelZ
    )
    txt += "X: %3d Y: %3d C: %s Z: %s" % (
      self.X, self.Y, [' ','*'][self.ButtonC], [' ','*'][self.ButtonZ]
    )
    return txt
  
  def __str__( self ):
    return self.__repr__debug__()


# --- some functions ---
def char_to_binary_string(self,char):
  ascii = ord(char)
  bin = []

  while (ascii > 0):
    if (ascii & 1) == 1:
      bin.append("1")
    else:
      bin.append("0")
    ascii = ascii >> 1
  
  bin.reverse()
  binary = "".join(bin)
  zerofix = (8 - len(binary)) * '0'
  
  return zerofix + binary

def i2bs(val):
	lst = []
	while val:
		lst.append(val&0xff)
		val = val >> 8
	lst.reverse()
	return lst

def l2hex(l):
  # pretty output of a int list (to hex list)
  hLst = []
  for val in l:
    hLst.append( hex(val) )
  return hLst

def popMultiple( buffer, num ):
  return list(buffer.pop(0) for i in range(num))

def dequeIndex( buffer, query ):
  # return the index of a value in a deque
  index = 0
  for k in buffer:
    if k == query:
      return index
    index += 1
  return index

def decryptBuffer( buffer ):
  returnBuffer = list()
  for i in buffer:
    j = (i^0x17) + 0x17
    returnBuffer.append( j )
  return returnBuffer

# --- the parsers ---
BUTTON_Two   = 0x0001
BUTTON_One   = 0x0002
BUTTON_B     = 0x0004
BUTTON_A     = 0x0008
BUTTON_Minus = 0x0010
BUTTON_Home  = 0x0080
BUTTON_Left  = 0x0100
BUTTON_Right = 0x0200
BUTTON_Down  = 0x0400
BUTTON_Up    = 0x0800
BUTTON_Plus  = 0x1000

def parseIr( buffer, wiiMoteState, size ):
  signal = popMultiple( buffer, size )
  if size == 12:
    wiiMoteState.IrRawX1 = signal[0] + ((signal[2] & 0x30) >> 4 << 8)
    wiiMoteState.IrRawY1 = signal[1] +  (signal[2]         >> 6 << 8)
    wiiMoteState.IrSize1 = signal[2] & 0x0f
    if wiiMoteState.IrRawY1 == 1023: wiiMoteState.IrFound1 = False
    else: wiiMoteState.IrFound1 = True
    
    wiiMoteState.IrRawX2 = signal[3] + ((signal[5] & 0x30) >> 4 << 8)
    wiiMoteState.IrRawY2 = signal[4] +  (signal[5]         >> 6 << 8)
    wiiMoteState.IrSize2 = signal[5] & 0x0f
    if wiiMoteState.IrRawY2 == 1023: wiiMoteState.IrFound2 = False
    else: wiiMoteState.IrFound2 = True
    
    wiiMoteState.IrRawX3 = signal[6] + ((signal[8] & 0x30) >> 4 << 8)
    wiiMoteState.IrRawY3 = signal[7] +  (signal[8]         >> 6 << 8)
    wiiMoteState.IrSize3 = signal[8] & 0x0f
    if wiiMoteState.IrRawY3 == 1023: wiiMoteState.IrFound3 = False
    else: wiiMoteState.IrFound3 = True 
    
    wiiMoteState.IrRawX4 = signal[9] + ((signal[11] & 0x30) >> 4 << 8)
    wiiMoteState.IrRawY4 = signal[10] + (signal[11]         >> 6 << 8)
    wiiMoteState.IrSize4 = signal[11] & 0x0f
    if wiiMoteState.IrRawY4 == 1023: wiiMoteState.IrFound4 = False
    else: wiiMoteState.IrFound4 = True
  
  elif size == 10:
    wiiMoteState.IrRawX1 = signal[0] + ((signal[2] & 0x30) >> 4 << 8)
    wiiMoteState.IrRawY1 = signal[1] + ((signal[2] & 0xc0) >> 6 << 8)
    wiiMoteState.IrSize1 = 5
    if wiiMoteState.IrRawY1 == 1023: wiiMoteState.IrFound1 = False
    else: wiiMoteState.IrFound1 = True
    
    wiiMoteState.IrRawX2 = signal[3] + ((signal[2] & 0x03)      << 8)
    wiiMoteState.IrRawY2 = signal[4] + ((signal[2] & 0x0c) >> 2 << 8)
    wiiMoteState.IrSize2 = 5
    if wiiMoteState.IrRawY2 == 1023: wiiMoteState.IrFound2 = False
    else: wiiMoteState.IrFound2 = True
    
    wiiMoteState.IrRawX3 = signal[5] + ((signal[7] & 0x30) >> 4 << 8)
    wiiMoteState.IrRawY3 = signal[6] + ((signal[7] & 0xc0) >> 6 << 8)
    wiiMoteState.IrSize3 = 5
    if wiiMoteState.IrRawY3 == 1023: wiiMoteState.IrFound3 = False
    else: wiiMoteState.IrFound3 = True
    
    wiiMoteState.IrRawX4 = signal[8] + ((signal[7] & 0x03)      << 8)
    wiiMoteState.IrRawY4 = signal[9] + ((signal[7] & 0x0c) >> 2 << 8)
    wiiMoteState.IrSize4 = 5
    if wiiMoteState.IrRawY4 == 1023: wiiMoteState.IrFound4 = False
    else: wiiMoteState.IrFound4 = True
  
  wiiMoteState.IrX1 = wiiMoteState.IrRawX1 / 1024.0
  wiiMoteState.IrY1 = wiiMoteState.IrRawY1 / 768.0
  
  wiiMoteState.IrX2 = wiiMoteState.IrRawX2 / 1024.0
  wiiMoteState.IrY2 = wiiMoteState.IrRawY2 / 768.0
  
  wiiMoteState.IrX3 = wiiMoteState.IrRawX3 / 1024.0
  wiiMoteState.IrY3 = wiiMoteState.IrRawY3 / 768.0
  
  wiiMoteState.IrX4 = wiiMoteState.IrRawX4 / 1024.0
  wiiMoteState.IrY4 = wiiMoteState.IrRawY1 / 768.0
  
  wiiMoteState.IrUpdated()
  
  return buffer

def parseAccel( buffer, wiimoteState ):
  signal = popMultiple( buffer, 3 )
  
  wiimoteState.AccelRawX = signal[0]
  wiimoteState.AccelRawY = signal[1]
  wiimoteState.AccelRawZ = signal[2]
  
  wiimoteState.AccelX = (wiimoteState.AccelRawX - wiimoteState.AccelCalibrationInfoX0) \
                      / (wiimoteState.AccelCalibrationInfoXG - wiimoteState.AccelCalibrationInfoX0)
  wiimoteState.AccelY = (wiimoteState.AccelRawY - wiimoteState.AccelCalibrationInfoY0) \
                      / (wiimoteState.AccelCalibrationInfoYG - wiimoteState.AccelCalibrationInfoY0)
  wiimoteState.AccelZ = (wiimoteState.AccelRawZ - wiimoteState.AccelCalibrationInfoZ0) \
                      / (wiimoteState.AccelCalibrationInfoZG - wiimoteState.AccelCalibrationInfoZ0)
  
  if wiimoteState.KEEP_HISTORY:
    wiimoteState.accelUpdated()
  
  return buffer

def parseButtons( buffer, wiiMoteState ):
  keybuffer = popMultiple( buffer, 2 )
  
  state = (keybuffer[0]<<8) + keybuffer[1]
  
  wiiMoteState.ButtonA     = bool(state&BUTTON_A)
  wiiMoteState.ButtonB     = bool(state&BUTTON_B)
  wiiMoteState.ButtonDown  = bool(state&BUTTON_Down)
  wiiMoteState.ButtonHome  = bool(state&BUTTON_Home)
  wiiMoteState.ButtonLeft  = bool(state&BUTTON_Left)
  wiiMoteState.ButtonMinus = bool(state&BUTTON_Minus)
  wiiMoteState.ButtonOne   = bool(state&BUTTON_One)
  wiiMoteState.ButtonPlus  = bool(state&BUTTON_Plus)
  wiiMoteState.ButtonRight = bool(state&BUTTON_Right)
  wiiMoteState.ButtonTwo   = bool(state&BUTTON_Two)
  wiiMoteState.ButtonUp    = bool(state&BUTTON_Up)
  wiiMoteState.ButtonState = state
  
  return buffer

def parseExtension( encryptedBuffer, extensionState ):
  if extensionState.__class__ == WIIMOTENUNCHUKEXTENSIONSTATE:
    buffer = decryptBuffer(encryptedBuffer)
    
    extensionState.RawX = buffer[0]
    extensionState.RawY = buffer[1]
    extensionState.AccelRawX = buffer[2]
    extensionState.AccelRawY = buffer[3]
    extensionState.AccelRawZ = buffer[4]
    extensionState.ButtonC = not((buffer[5] & 0x02) >> 1)
    extensionState.ButtonZ = not(buffer[5] & 0x01)
    
    extensionState.AccelX = (float(extensionState.AccelRawX) - extensionState.AccelCalibrationX0) \
                          / (extensionState.AccelCalibrationXG - extensionState.AccelCalibrationX0)
    #print extensionState.AccelX, extensionState.AccelRawX, extensionState.AccelCalibrationX0, extensionState.AccelCalibrationXG
    extensionState.AccelY = (float(extensionState.AccelRawY) - extensionState.AccelCalibrationY0) \
                          / (extensionState.AccelCalibrationYG - extensionState.AccelCalibrationY0)
    extensionState.AccelZ = (float(extensionState.AccelRawZ) - extensionState.AccelCalibrationZ0) \
                          / (extensionState.AccelCalibrationZG - extensionState.AccelCalibrationZ0)
    
    extensionState.X  = (extensionState.RawX - extensionState.MidX) \
                      / float(extensionState.MaxX - extensionState.MinX)
    extensionState.Y  = (extensionState.RawY - extensionState.MidY) \
                      / float(extensionState.MaxY - extensionState.MinY)
  
  return encryptedBuffer

def readReport( memoryReport ):
  error = memoryReport[0] & 0x0f
  if error == 8:
    print "W: readReport: error bit set (address don't exist)"
  elif error == 7:
    print "W: readReport: error bit set (write-only registers)"
  elif error == 0:
    pass
#    print "I: readReport: error bit not set"
  else:
    print "E: readReport: error bit is invalid", error
  size = ((memoryReport[0] & 0xf0) >> 4)
#  print "I: readReport: package size", size
  packageOffset = memoryReport[1] << 8 + memoryReport[2]
  if size == 1:
    header = popMultiple( memoryReport, 2 )
  else:
    header = popMultiple( memoryReport, 3 )
  data = popMultiple( memoryReport, size )
  return data

INPUTREPORT_DATA =  { "InputReportStatus"                     : [ 0x20,  8 ]
                    , "InputReportReadData"                   : [ 0x21, 23 ]
                    , "InputReportUnknown1"                   : [ 0x22,  6 ]
                    , "InputReportButtons"                    : [ 0x30,  4 ]
                    , "InputReportButtonsAccel"               : [ 0x31,  7 ]
                    , "InputReportButtonsExtension8"          : [ 0x32, 12 ]
                    , "InputReportButtonsAccelIR12"           : [ 0x33, 19 ]
                    , "InputReportButtonsExtension19"         : [ 0x34, 23 ]
                    , "InputReportButtonsAccelExtension16"    : [ 0x35, 23 ]
                    , "InputReportButtonsIR10Extension9"      : [ 0x36, 23 ]
                    , "InputReportButtonsAccelIR10Extension6" : [ 0x37, 23 ]
                    , "InputReportButtonsAccelIR"             : [ 0x38, 23 ]
                    , "InputReportExtension21"                : [ 0x3d, 23 ]
                    , "InputReportInterleaved1"               : [ 0x3e, 23 ]
                    , "InputReportInterleaved2"               : [ 0x3f, 23 ] }

INREPORT_V2L = dict(); INREPORT_N2V = dict(); INREPORT_V2N = dict()
for n, [v, l] in INPUTREPORT_DATA.items():
  INREPORT_V2L[v] = l
  INREPORT_N2V[n] = v
  INREPORT_V2N[v] = n


if sys.platform == 'darwin':
  import lightblue
  class systemWiimoteLibClass:
    def __init__( self ):
      print "wiimoteLib2.systemWiimoteLibClass: using darwin wiimote subsystem"
      self.isThreaded = False
    
    def __init_done__( self ):
      # is used on windows for thrading
      pass
    
    def run( self ):
      pass
    
    def atExit( self ):
      print "I: wiimoteLibClass.atExit: closing sockets, deleting sockets"
      #self.isocket.shutdown(2) # 0 = done receiving, 1 = done sending, 2 = both
      self.isocket.close()
      #self.osocket.shutdown(2)
      self.osocket.close()
      #self.tempSocket.shutdown(2)
      #self.tempSocket.close()
      
      del self.isocket
      del self.osocket
      #del self.tempSocket
      
      #print "wiimotelib2.how to quit lightblue???"
      #print dir(lightblue)
    
    def search( self ):
      print "I: wiimoteLibClass.search: Searching for a Wiimote"
      if not self.connected:
        print "I: wiimoteLibClass.search: Press 1 and 2 on your Wiimote to make it discoverable."
        devicelist = lightblue.finddevices()
        found = list()
        for address, name, device_class in devicelist:
          if name == u'Nintendo RVL-CNT-01':
            found.append( address )
        if len(found) > 0:
          print "I: wiimoteLibClass.search: Found a wiimote", address
          self.found = address
        else:
          print "I: wiimoteLibClass.search: Couldn't find wiimote. Exiting"
          raise SystemExit
        return self.found
      else:
        print "I: wiimoteLibClass.search: already connected to a wiimote"
    
    def connect(self, bd_addr=None):
      if bd_addr is None:
        # use bluetooth DeviceDiscoverer
        self.bd_addr = self.search()
        # necessary before opening socket !
        time.sleep(1)
      else:
        self.bd_addr = bd_addr
      
      print "I: wiimoteLibClass.connect: creating sockets for", self.bd_addr
      
      self.isocket = lightblue.socket(lightblue.L2CAP)
      #self.tempSocket = lightblue.socket(lightblue.L2CAP)
      self.osocket = lightblue.socket(lightblue.L2CAP)
      
      print "I: wiimoteLibClass.connect: connecting to", self.bd_addr
      
      time.sleep(0.1)
      connected = False
      while not connected:
        try:
          self.isocket.connect((self.bd_addr,19))
          print "I: wiimoteLibClass.connect: socket connect success for",self.bd_addr,19
          connected = True
        except:
          print "I: wiimoteLibClass.connect: socket connect failed for",self.bd_addr,19
          time.sleep(5.0)
          print "I: - if this never succeeds, remove the nintendo controller from"
          print "     the bluetooth connections in the osx configuration"
      
      time.sleep(0.1)
      connected = False
      while not connected:
        try:
          self.osocket.connect((self.bd_addr,17))
          print "I: wiimoteLibClass.connect: socket connect success for",self.bd_addr,17
          connected = True
        except:
          print "I: wiimoteLibClass.connect: socket connect failed for",self.bd_addr,17
          time.sleep(5.0)
      self.connected = True
      
      print "I: wiimoteLibClass.connect: setting socket timeouts"
      
      try:
        self.isocket.settimeout(0.001) #0.0000000001
      except NotImplementedError:
        print "I: wiimoteLibClass.connect: socket i timeout not implemented with this bluetooth module"
      except socket.error:
        pass
      try:
        self.osocket.settimeout(10.0)
      except NotImplementedError:
        print "I: wiimoteLibClass.connect: socket o timeout not implemented with this bluetooth module"
      except socket.error:
        pass
      
      print "I: wiimoteLibClass.connect: wiimote successfully connected", self.bd_addr
      self.running = True
  
    def readSocketData( self ):
      # receive all data from the controller
      try:
        data = self.isocket.recv(1024)
      except lightblue.BluetoothError:
        data = list()
      except socket.timeout:
        data = list()
      except socket.error:
        data = list()
      return data
  
    def _send_data( self, data ):
      str_data = ""
      for each in data:
        str_data += chr(each)
      if True:
      #try:
        self.osocket.send(str_data)
        return True
      #except socket.error:
      #  print "E: WiimoteLib._send_data: sending data to wiimote failed %s" % str(data)
      #  return False
  
elif sys.platform == 'win32' or sys.platform == 'linux2':
  import bluetooth
  from bluetooth import *
  from threading import Thread
  class systemWiimoteLibClass(Thread):
    def __init__( self ):
      Thread.__init__( self )
      self.isThreaded = True
      print "wiimoteLib2.systemWiimoteLibClass: using windows driver subsystem"
    
    def __init_done__( self ):
      self.start()
    
    def run( self ):
      '''# for threaded, but does not work
      import objc
      # Most useful systems will at least have 'NSObject'.
      NSObject = objc.lookUpClass('NSObject')
      NSArray = objc.lookUpClass('NSArray')
      NSAutoreleasePool = objc.lookUpClass('NSAutoreleasePool')
      #rt = pyobjc.runtime	# shorthand -- runtime gets used a lot!
      pool = NSAutoreleasePool.alloc().init()'''
      while True:
        self.readData()
    
    def connect( self, bd_addr=None):
      if bd_addr is None:
        print "wiimoteLib2.systemWiimoteLibClass (win32): none address not implemented"
        raise
      else:
        self.bd_addr = bd_addr
      
      print "I: wiimoteLibClass.connect: creating sockets for", self.bd_addr
      
      try:
        self.isocket = bluetooth.BluetoothSocket(bluetooth.L2CAP)
        self.osocket = bluetooth.BluetoothSocket(bluetooth.L2CAP)
      except:
        print "E: The native Windows bluetooth stack doesnt support L2CAP connections."
        print "E: - get yourself a bluetooth dongle with a widcomm driver (5.1.0.x)"
        print "E: - microsoft was just not able to implement this protocol itself"
        raise
      
      print "I: wiimoteLibClass.connect: connecting to", self.bd_addr
      
      #self.isocket.connect((self.bd_addr,19))
      time.sleep(0.1)
      connected = False
      while not connected:
        try:
          self.isocket.connect((self.bd_addr,19))
          print "I: wiimoteLibClass.connect: socket connect success for",self.bd_addr,19
          connected = True
        except:
          print "I: wiimoteLibClass.connect: socket connect failed for",self.bd_addr,19
          time.sleep(5.0)
          print "I: - if this never succeeds, remove the nintendo controller from"
          print "     the bluetooth connections in the osx configuration"
      
      time.sleep(0.1)
      connected = False
      while not connected:
        try:
          self.osocket.connect((self.bd_addr,17))
          print "I: wiimoteLibClass.connect: socket connect success for",self.bd_addr,17
          connected = True
        except:
          print "I: wiimoteLibClass.connect: socket connect failed for",self.bd_addr,17
          time.sleep(5.0)
      self.connected = True
      
      print "I: wiimoteLibClass.connect: setting socket timeouts"
      
      try:
        self.isocket.settimeout(0.0001) #0.0000000001
      except NotImplementedError:
        print "I: wiimoteLibClass.connect: socket i timeout not implemented with this bluetooth module"
      try:
        self.osocket.settimeout(1.0)
      except NotImplementedError:
        print "I: wiimoteLibClass.connect: socket o timeout not implemented with this bluetooth module"
      
      print "I: wiimoteLibClass.connect: wiimote successfully connected", self.bd_addr
      self.running = True
  

    def readSocketData( self ):
      # receive all data from the controller
      try:
        data = self.isocket.recv(1024)
      except:
        data = list()
      return data
    
    def _send_data( self, data ):
      str_data = ""
      for each in data:
        str_data += chr(each)
      #if True:
      try:
        self.osocket.send(str_data)
        return True
      except socket.error:
        print "E: WiimoteLib._send_data: sending data to wiimote failed %s" % str(data)
        return False
    
    def atExit( self ):
      print "I: wiimoteLibClass.atExit: closing sockets, deleting sockets"
      self.isocket.shutdown(2) # 0 = done receiving, 1 = done sending, 2 = both
      self.isocket.close()
      self.osocket.shutdown(2)
      self.osocket.close()
      
      del self.isocket
      del self.osocket
  
class wiimoteLibClass( systemWiimoteLibClass ):
  def __init__( self, uid=None ):
    systemWiimoteLibClass.__init__( self )
    
    # storage containers for received data
    self.wiiMoteState = WIIMOTESTATE()
    self.extensionState = WIIMOTEDUMMYEXTENSIONSTATE()
    
    self.data = list()
    self.connected = False
    self.packageCount = 0
    
    if uid is not None:
      self.connectToUid( uid )
    
    systemWiimoteLibClass.__init_done__( self )
  
  def keepHistory( self, setting ):
    self.wiiMoteState.KEEP_HISTORY = setting
  
  def connectToUid( self, uid ):
    
    if not self.connected:
      self.connect( uid )
      
      atexit.register( self.atExit )
      
      # set leds disabled (must be done just after connecting, else they blink forever)
      state = self.SetLEDs( 0, 0, 0, 0 )
      if state is False:
        print "I: wiimoteLibClass.connectToUid: send data test failed, trying again"
        import sys
        sys.exit()
      
      # accept initializing package (this must be done, if a extension is already plugged in)
      for i in xrange(10):
        self.step()
      
      self.readCalibration()
      
      # set default settings (ir/accel/continuous disabled)
      self.updateIrMode()
      
      for i in xrange(10):
        self.step()
      
    else:
      state = True
      print "W: wiimoteLibClass: already connected"
    
    return state
  
  def readData( self ):
    self.data.extend( map(ord, self.readSocketData() ) )
  
  def step( self ):
    self.readData()
    return self.parseData()
  
  def parseData( self ):
    while len(self.data)>=2:
      retVal = self.parseDataStep()
      if retVal is not None:
        return retVal
      if retVal == -1:
        return None
    return None
  
  def parseDataStep( self ):
    if len(self.data)>=2:
      pkgType = self.data[1]
      # check package
      
      try:
        if len(self.data) >= INREPORT_V2L[pkgType]:
          package = popMultiple( self.data, INREPORT_V2L[pkgType])
        else:
          print "W: wiimoteLibClass.parseData: invalid or unknown package", hex(pkgType), len(self.data), INREPORT_V2L[pkgType], self.data
          return -1
      except:
        print "E: wiimoteLibClass.parseData: invalid data"
        print "E: - ", self.data
        print "E: - ", l2hex(self.data)
        raise
      
      self.packageCount += 1
      
      tmp = popMultiple( package, 2)
      if   pkgType == INREPORT_N2V['InputReportStatus']:
        package = parseButtons( package, self.wiiMoteState )
        print "W: wiimoteLibClass.parseData: InputReportStatus, pkg", l2hex(package)
        
        # get the real LED values in case the values from SetLEDs() somehow becomes out of sync, which really shouldn't be possible
        self.wiiMoteState.LED1    = (package[1] & 0x10) != 0
        self.wiiMoteState.LED2    = (package[1] & 0x20) != 0
        self.wiiMoteState.LED3    = (package[1] & 0x40) != 0
        self.wiiMoteState.LED4    = (package[1] & 0x80) != 0
        self.wiiMoteState.BATTERY = package[3]
        extensionPluggedIn        = (package[1] & 0x02) != 0
        speakerEnabled            = (package[1] & 0x04) != 0
        continuousOutputMode      = (package[1] & 0x08) != 0
        self.initializeExtension()
      elif pkgType == INREPORT_N2V['InputReportReadData']:
        package = parseButtons( package, self.wiiMoteState )
        return package
      elif pkgType == INREPORT_N2V['InputReportButtons']:
        package = parseButtons( package, self.wiiMoteState )
      elif pkgType == INREPORT_N2V['InputReportButtonsAccel']:
        package = parseButtons( package, self.wiiMoteState )
        package = parseAccel( package, self.wiiMoteState )
      elif pkgType == INREPORT_N2V['InputReportButtonsExtension8']:
        package = parseButtons( package, self.wiiMoteState )
        package = parseExtension( package, self.extensionState )
      elif pkgType == INREPORT_N2V['InputReportButtonsAccelIR12']:
        package = parseButtons( package, self.wiiMoteState )
        package = parseAccel( package, self.wiiMoteState )
        package = parseIr( package, self.wiiMoteState, 12 )
      elif pkgType == INREPORT_N2V['InputReportButtonsExtension19']:
        package = parseButtons( package, self.wiiMoteState )
        package = parseExtension( package, self.extensionState )
      elif pkgType == INREPORT_N2V['InputReportButtonsAccelExtension16']:
        package = parseButtons( package, self.wiiMoteState )
        package = parseAccel( package, self.wiiMoteState )
        package = parseExtension( package, self.extensionState )
      elif pkgType == INREPORT_N2V['InputReportButtonsIR10Extension9']:
        package = parseButtons( package, self.wiiMoteState )
        package = parseIr( package, self.wiiMoteState, 10 )
        package = parseExtension( package, self.extensionState )
      elif pkgType == INREPORT_N2V['InputReportButtonsAccelIR10Extension6']:
        package = parseButtons( package, self.wiiMoteState )
        package = parseAccel( package, self.wiiMoteState )
        package = parseIr( package, self.wiiMoteState, 10 )
        package = parseExtension( package, self.extensionState )
      elif pkgType == INREPORT_N2V['InputReportButtonsAccelIR']:
        package = parseButtons( package, self.wiiMoteState )
        package = parseAccel( package, self.wiiMoteState )
        package = parseIr( package, self.wiiMoteState )
      elif pkgType == INREPORT_N2V['InputReportExtension21']:
        package = parseExtension( package, self.extensionState )
      elif pkgType == INREPORT_N2V['InputReportInterleaved1']:
        pass
      elif pkgType == INREPORT_N2V['InputReportInterleaved2']:
        pass
      else:
        print "W: wiimoteLib2: unknown package type:", pkgType, l2hex(package)
    else:
      return -1
  
  def initializeExtension( self ):
    # initialize the extension
    self._send_data( i2bs(0x521604A400400100000000000000000000000000000000) )
    
    extension = self._read_from_mem(REGISTER_EXTENSION_TYPE, 2)
    if extension[0] == ExtensionTypeNunchuk:
      print "I: wiimoteLibClass.initializeExtension: found nunchuk extension controller"
      self.extensionState = WIIMOTENUNCHUKEXTENSIONSTATE()
      self.wiiMoteState.SEND_EXTENSION = True
    elif extension[0] == ExtensionTypeClassicController:
      print "I: wiimoteLibClass.initializeExtension: found classic extension controller (not handled)"
      self.extensionState = WIIMOTEDUMMYEXTENSIONSTATE()
      self.wiiMoteState.SEND_EXTENSION = True
    elif extension[0] == 0xff:
      print "I: wiimoteLibClass.initializeExtension: partially inserted"
      self.extensionState = WIIMOTEDUMMYEXTENSIONSTATE()
      self.wiiMoteState.SEND_EXTENSION = False
    else:
      print "I: wiimoteLibClass.initializeExtension: unknown or none inserted %s" % hex(extension[0])
      self.extensionState = WIIMOTEDUMMYEXTENSIONSTATE()
      self.wiiMoteState.SEND_EXTENSION = False
    
    extensionCalibration = decryptBuffer(self._read_from_mem(REGISTER_EXTENSION_CALIBRATION, 16))
    
    if self.extensionState.__class__ == WIIMOTENUNCHUKEXTENSIONSTATE:
      self.extensionState.AccelCalibrationX0 = float(extensionCalibration[0])
      self.extensionState.AccelCalibrationY0 = float(extensionCalibration[1])
      self.extensionState.AccelCalibrationZ0 = float(extensionCalibration[2])
      self.extensionState.AccelCalibrationXG = float(extensionCalibration[4])
      self.extensionState.AccelCalibrationYG = float(extensionCalibration[5])
      self.extensionState.AccelCalibrationZG = float(extensionCalibration[6])
      self.extensionState.MaxX = float(extensionCalibration[8])
      self.extensionState.MinX = float(extensionCalibration[9])
      self.extensionState.MidX = float(extensionCalibration[10])
      self.extensionState.MaxY = float(extensionCalibration[11])
      self.extensionState.MinY = float(extensionCalibration[12])
      self.extensionState.MidY = float(extensionCalibration[13])
    
    self.updateIrMode()
    self._send_data([0x52]+i2bs(0x1200)+[self.wiiMoteState.getSendSignal()])
  
  def SetLEDs(self, led1,led2,led3,led4):
    self.wiiMoteState.LED1 = led1
    self.wiiMoteState.LED2 = led2
    self.wiiMoteState.LED3 = led3
    self.wiiMoteState.LED4 = led4
    return self.updateRumbleLED()
  
  def SetRumble(self,on):
    self.wiiMoteState.RUMBLE = on
    self.updateRumbleLED()
  
  def updateRumbleLED( self ):
    ledSignal = self.wiiMoteState.getLedSignal()
    return self._send_data((0x52,OutputReportLEDs,ledSignal))
  
  def setContinuous( self, continous = True ):
    print "I: p3f.src.wiimote.set_continuous"
    self.wiiMoteState.SEND_CONTINUOUS = continous
    #self._send_data([0x52]+i2bs(0x12)+[self.wiiMoteState.getTransmissionSignal(), self.wiiMoteState.getSendSignal()])
    #time.sleep(0.1)
    self.updateMode()
  
  def setAccel( self, state ):
    print "I: p3f.src.wiimote.setAccel: state %s" % (['off','on'][state])
    self.wiiMoteState.SEND_ACCEL = state
    self.updateMode()
  
  def activate_accel(self):
    print "I: p3f.src.wiimote.activate_accel: is depricated, use setAccel instead"
    self.setAccel( True )
  
  def setIr( self, state, sensitivity='max' ):
    print "I: p3f.src.wiimote.setIr: state %s sensitivity %s" % (['off','on'][state], str(sensitivity))
    if state:
      print "I: - activating accel as well, cause ir alone doesnt work"
      self.setAccel( True )
    self.wiiMoteState.SEND_IR = state
    if sensitivity not in SIGNAL_IR_SENSITIVITY:
      print "wiimoteLib2.wiimoteLibClass.setIr: INVALID SENSITIVITY", sensitivity, "must be one of", SIGNAL_IR_SENSITIVITY.keys()
      raise Exception
    self.wiiMoteState.IR_SENSITIVITY = sensitivity # 0 some state, 1 maximum sensitivity
    self.updateIrMode()
  
  def activate_IR(self, maxsensitivity = False ):
    print "I: p3f.src.wiimote.activate_IR:  is depricated, use setIr instead"
    if maxsensitivity:  # sensitivity must be one of SIGNAL_IR_SENSITIVITY
      sensitivity = 'max'
    else:
      sensitivity = 'lvl3'
    self.setIr( True, sensitivity )
  
  def updateIrMode( self ):
    self.updateMode()
    time.sleep(0.1)
    self._send_data([0x52]+i2bs(0x1304))
    time.sleep(0.1)
    self._send_data([0x52]+i2bs(0x1a04))
    time.sleep(0.1)
    self._write_to_mem(REGISTER_IR,0x08)                               # 0x04b00030
    time.sleep(0.1)
    
    # sensitivity
    for addr, signal, signalLength in self.wiiMoteState.getSensitivitySignals():
      self._write_to_mem(addr, signal, signalLength)
      time.sleep(0.1)
    
    time.sleep(0.1)
    self._write_to_mem(REGISTER_IR_MODE, self.wiiMoteState.getIrMode()) # 0x04b00033
    
    time.sleep(0.1)
    self.updateMode()
  
  def updateMode( self ):
    time.sleep(0.1)
    self._send_data([0x52]+i2bs(0x12)+[self.wiiMoteState.getTransmissionSignal(), self.wiiMoteState.getSendSignal()])
  
  def _get_battery_status(self):
    data = self.requestData( (0x52,0x15,0x00), 7 )
    print len(data), data
    battery_level = (100*data[7])/206
    self.wiiMoteState.BATTERY = battery_level
    
    if False: # check if requestdata works correctly
      self._send_data((0x52,0x15,0x00))
      self.running2 = True
      while self.running2:
        try:
          x= map(ord,self.isocket.recv(32))
        except lightblue.BluetoothError:
          continue
        except socket.timeout:
          continue
        self.state = ""
        for each in x[:17]:
          if len(x) >= 7:
            self.running2 = False
            battery_level = (100*x[7])/206
      if self.wiiMoteState.BATTERY != battery_level:
        print "E: _get_battery_status: is wrong", self.wiiMoteState.BATTERY, battery_level
        raise
      else:
        print "I: _get_battery_status: is correct", self.wiiMoteState.BATTERY, battery_level
        raise
  
  def readCalibration( self ):
    # equivalent to wiimote.ReadCalibration
    parseData = self._read_from_mem( 0x0016, 7 )
    
    if len(parseData) == 6:
      self.wiiMoteState.AccelCalibrationInfoX0 = float(parseData[0])
      self.wiiMoteState.AccelCalibrationInfoY0 = float(parseData[1])
      self.wiiMoteState.AccelCalibrationInfoZ0 = float(parseData[2])
      self.wiiMoteState.AccelCalibrationInfoXG = float(parseData[3])
      self.wiiMoteState.AccelCalibrationInfoYG = float(parseData[4])
      self.wiiMoteState.AccelCalibrationInfoZG = float(parseData[5])
    else:
      print "E: wiimoteLibClass.readCalibration: invalid data", parseData
  
  def getWiimoteAttr( self, value ):
    #if value==0:
    return getattr( self.wiiMoteState, value )
  
  def _write_to_mem(self, address, value, val_len=None):
#    print "I: wiimoteLibClass._write_to_mem: ", hex(address), ":", hex(value)
    val = i2bs(value)
    if val_len is None:
      # calculate value length if not defined
      val_len=len(val)
    val += [0]*(16-val_len)
    msg = [0x52,OutputReportWriteMemory] + i2bs(address) + [val_len] + val
    self._send_data(msg)
  
  def _read_from_mem( self, address, size ):
    ''' starts another read loop, and exits if a memory read response happens
    '''
    # equivalent to wiimote.ReadData
    adr = i2bs(address)
    adr_len = len(adr)
    adr = [0]*(4-adr_len) + adr
    siz = i2bs(size)
    siz_len = len(siz)
    siz = [0]*(2-siz_len) + siz
    sendData = [0x52, OutputReportReadMemory] + adr + siz
    self._send_data( sendData )
    
    running = True
    timer = time.time()
    x = 0
    while running:
      if (time.time()-timer < 1.0):
        returnData = self.step()
        if returnData is not None:
          if len(returnData) >= size:
            parseData = readReport( returnData )
            return parseData
      else:
        print "E: wiimoteLibClass._read_from_mem: timeout", address, size
        return list()
    

  
def testApp( wiimoteId ):
  if False:
    wiimoteId = lightblue.selectdevice()
    wiimote = wiimoteLibClass( wiimoteId[0] )
    print "using nintendo wiimote with address", wiimoteId[0]
  else:
    print "press 1&2 on the wiimote to make it discoverable"
    #wiimote = wiimoteLibClass( '00:19:1D:B7:F4:27' )
    wiimote = wiimoteLibClass( wiimoteId )
  
  print "wiimote connection succeeded"
  
  debug = pygameDebug( wiimote )
  
  if True:
    wiimote.setIr( True, 'max' )
    wiimote.setContinuous( True )
  
  if False:
    print "set rumble"
    wiimote.wiiMoteState.RUMBLE = True
    wiimote.SetLEDs( 1,0,0,0 )
    time.sleep( 0.1 )
  
  debug.run()

class pygameDebug:
  def __init__( self, wiimote ):
    self.wiimote = wiimote
    if not hasPygame:
      print "pygameDebug requires pygame"
      raise
    pygame.init()
    size = width, height = 256, 256
    self.screen = pygame.display.set_mode(size)
    self.font = pygame.font.Font(None, 17)
    
    # calculate fps
    self.t = time.time()
    self.c = 0
    self.lastPackageCount = 0
    
    # rendering stuff
    self.colors = {
      'wiiX': (255,0,0)
    , 'wiiY': (0,255,0)
    , 'wiiZ': (0,0,255)
    , 'nunchuckX': (255,255,0)
    , 'nunchuckY': (255,0,255)
    , 'nunchuckZ': (0,255,255)
    }
    self.lines = dict()
    self.lines['wiiX'] = list()
    self.lines['wiiY'] = list()
    self.lines['wiiZ'] = list()
    self.lines['nunchuckX'] = list()
    self.lines['nunchuckY'] = list()
    self.lines['nunchuckZ'] = list()
    
    for k,v in self.lines.items():
      for i in xrange(256):
        v.append(0)
    
    self.activeButtons = list()
    
  def run( self ):
    while 1:
      self.step()
      self.calcFps()
  
  def calcFps( self ):
    self.c += 1
    if time.time() - self.t > 1.0:
      dt = time.time() - self.t
      print "fps", self.c/dt
      self.t = time.time()
      self.c = 0
      print "packages received", self.wiimote.packageCount - self.lastPackageCount
      self.lastPackageCount = self.wiimote.packageCount
  
  def step( self ):
    
    CONSOLE_OUTPUT = False
    
    self.wiimote.step()
    
    if self.wiimote.wiiMoteState.ButtonA:
      print self.wiimote.wiiMoteState
    
    # edit sensitivity
    if True:
      currentSetting = self.wiimote.wiiMoteState.IR_SENSITIVITY
      currentSettingNumber = SIGNAL_IR_SENSITIVITY_SORTED_LIST.index( currentSetting )
      maxLen = len(SIGNAL_IR_SENSITIVITY_SORTED_LIST)
      print "EDIT SENSITIVITY", currentSetting, currentSettingNumber, maxLen
      # increase sensitivity
      if self.wiimote.wiiMoteState.ButtonPlus:
        if '+' not in self.activeButtons:
          # do it here
          self.wiimote.setIr( True, SIGNAL_IR_SENSITIVITY_SORTED_LIST[ min(maxLen-1, max(currentSettingNumber+1, 0) ) ] )
          self.activeButtons.append( "+" )
      else:
        if "+" in self.activeButtons:
          self.activeButtons.remove( "+" )
      
      # decrease sensitivity
      if self.wiimote.wiiMoteState.ButtonMinus:
        if '-' not in self.activeButtons:
          # do it here
          self.wiimote.setIr( True, SIGNAL_IR_SENSITIVITY_SORTED_LIST[ min(maxLen-1, max(currentSettingNumber-1, 0) ) ] )
          self.activeButtons.append( "-" )
      else:
        if "-" in self.activeButtons:
          self.activeButtons.remove( "-" )
    
    if True:
      l1, l2, l3, l4 = 0,0,0,0
      
      if False:
        print self.wiimote.wiiMoteState
      
      wiimote = self.wiimote
      
      points = list()
      if wiimote.wiiMoteState.IrFound1:
        points.append( [wiimote.wiiMoteState.IrRawX1, wiimote.wiiMoteState.IrRawY1, wiimote.wiiMoteState.IrSize1, (255,0,0)] )
      if wiimote.wiiMoteState.IrFound2:
        points.append( [wiimote.wiiMoteState.IrRawX2, wiimote.wiiMoteState.IrRawY2, wiimote.wiiMoteState.IrSize2, (255,0,0)] )
      if wiimote.wiiMoteState.IrFound3:
        points.append( [wiimote.wiiMoteState.IrRawX3, wiimote.wiiMoteState.IrRawY3, wiimote.wiiMoteState.IrSize3, (255,0,0)] )
      if wiimote.wiiMoteState.IrFound4:
        points.append( [wiimote.wiiMoteState.IrRawX4, wiimote.wiiMoteState.IrRawY4, wiimote.wiiMoteState.IrSize4, (255,0,0)] )
      
      # output wiiMoteState
      self.screen.fill((0,0,0))
      
      # show key state
      text = self.font.render( wiimote.wiiMoteState.__repr__(), True, (255, 255, 255), (159, 182, 205))
      textRect = text.get_rect()
      textRect.centerx = self.screen.get_rect().centerx
      textRect.centery = self.screen.get_rect().centery
      self.screen.blit(text, textRect)
      
      self.lines['wiiX'].append( wiimote.wiiMoteState.AccelX )
      self.lines['wiiX'].pop(0)
      self.lines['wiiY'].append( wiimote.wiiMoteState.AccelY )
      self.lines['wiiY'].pop(0)
      self.lines['wiiZ'].append( wiimote.wiiMoteState.AccelZ )
      self.lines['wiiZ'].pop(0)
      
      points.append( [  5, wiimote.wiiMoteState.AccelX*128+128, 1, (0,255,0) ] )
      points.append( [ 15, wiimote.wiiMoteState.AccelY*128+128, 1, (0,255,0) ] )
      points.append( [ 25, wiimote.wiiMoteState.AccelZ*128+128, 1, (0,255,0) ] )
      if CONSOLE_OUTPUT:
        print "wiimote.wiiMoteState", wiimote.wiiMoteState
      
      if wiimote.extensionState.__class__ == WIIMOTENUNCHUKEXTENSIONSTATE:
        points.append( [ wiimote.extensionState.X*128+128, wiimote.extensionState.Y*128+128, 1, (0,0,255) ] )
        points.append( [ 35, wiimote.extensionState.AccelX*128+128, 1, (0,0,255) ] )
        points.append( [ 45, wiimote.extensionState.AccelY*128+128, 1, (0,0,255) ] )
        points.append( [ 55, wiimote.extensionState.AccelZ*128+128, 1, (0,0,255) ] )
        
        self.lines['nunchuckX'].append( wiimote.extensionState.AccelX )
        self.lines['nunchuckX'].pop(0)
        self.lines['nunchuckY'].append( wiimote.extensionState.AccelY )
        self.lines['nunchuckY'].pop(0)
        self.lines['nunchuckZ'].append( wiimote.extensionState.AccelZ )
        self.lines['nunchuckZ'].pop(0)
        
        if CONSOLE_OUTPUT:
          print "wiimote.extensionState", wiimote.extensionState
      
      # show ir data
      for x,y,r,c in points:
        pygame.draw.circle(self.screen, c, (int(x/4),int(y/4)), r, 0)
      
      for k,v in self.lines.items():
        prev = 128
        for i in xrange(256):
          p = int(v[i]*32+128)
          pygame.draw.line(self.screen, self.colors[k], (i-1,prev),(i,p), 1 )
          prev = p
      
      pygame.display.flip()
      
      if True:
        if True:
          l1 = int(time.time()) & 0x01
          l2 = int(time.time()) & 0x02
          l3 = int(time.time()) & 0x04
          l4 = int(time.time()) & 0x08
        else:
          l1, l2, l3, l4 = 0,0,0,0
        wiimote.SetLEDs( l1, l2, l3, l4 )
    


if __name__ == "__main__":
  hasPygame = False
  try:
    import pygame
    hasPygame = True
  except:
    print "testing app requires pygame (for visual debug ouput)"
  
  if True:
    #p = testApp( '00:19:1D:B7:F4:27' )
    p = testApp( '00:1D:BC:2D:35:1E' )
  else:
    wiimote1 = wiimoteLibClass( '00:1D:BC:2D:35:1E' ) # hand
    wiimote1.activate_accel()
    wiimote1.activate_IR()
    wiimote2 = wiimoteLibClass( '00:19:1D:B7:F4:27' ) # tracking
    wiimote2.activate_accel()
    wiimote2.activate_IR()
    #wiimote1.start()
    #wiimote2.start()
    #wiimote1.step()
    wiimote1.setContinuous( True )
    #wiimote2.step()
    wiimote2.setContinuous( True )
    t = time.time()
    c = 0
    while True:
      time.sleep(0.05)
      wiimote1.step()
      wiimote2.step()
      if time.time() - t > 1.0:
        t = time.time()
        print "FPS:", c
        c = 0
      c += 1
      wiimote1.SetRumble( wiimote2.wiiMoteState.ButtonB )
      wiimote2.SetRumble( wiimote1.wiiMoteState.ButtonB )

[edit] Small Fix

This is most likely fixed in the new version (4.June.08), but i did not actually check it.

In parseButtons() the Button-Values are named A, B .. :

wiiMoteState.A     = bool(state&BUTTON_A)

but in WIIMOTESTATE.__repr__() the Button-Values are accessed as ButtonA, ButtonB .. :

txt = "Button: %s:%s %s:%s " % ("A", [' ','*'][self.ButtonA], "B", [' ','*'][self.ButtonB])

Thus the correct values are never displayed, the following changes have to be made in order to show them:

	txt = "Button: %s:%s %s:%s " % ("A", [' ','*'][self.A], "B", [' ','*'][self.B])
	txt += "%s:%s %s:%s " % ("^", [' ','*'][self.Up], "v", [' ','*'][self.Down]) 
	txt += "%s:%s %s:%s " % ("<", [' ','*'][self.Left], ">", [' ','*'][self.Right])
	txt += "%s:%s %s:%s " % ("-", [' ','*'][self.Minus], "+", [' ','*'][self.Plus])
	txt += "%s:%s %s:%s " % ("1", [' ','*'][self.One], "2", [' ','*'][self.Two])
	txt += "%s:%s\n" % ("Home", [' ','*'][self.Home])

[edit] Some additional notes

EDIT: i uploaded a new version. The old text is deprecated.

Personal tools
Online Casino - best online casino reviews.
Facebook Developers - facebook applications, facebook developers, facebook development, social network application development and viral widget social media strategy