| 1 | #!/usr/bin/python |
|---|
| 2 | # |
|---|
| 3 | # wiimote.py - Wii Remote data inspector. This will be used as a learning |
|---|
| 4 | # framework until we have enough data to write an actual wiimote driver. |
|---|
| 5 | # |
|---|
| 6 | # Copyright (C) 2007 Will Woods <wwoods@redhat.com> |
|---|
| 7 | # |
|---|
| 8 | # This program is free software; you can redistribute it and/or |
|---|
| 9 | # modify it under the terms of the GNU General Public License |
|---|
| 10 | # as published by the Free Software Foundation; either version 2 |
|---|
| 11 | # of the License, or (at your option) any later version. |
|---|
| 12 | # |
|---|
| 13 | # This program is distributed in the hope that it will be useful, |
|---|
| 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 16 | # GNU General Public License for more details. |
|---|
| 17 | # |
|---|
| 18 | # You should have received a copy of the GNU General Public License |
|---|
| 19 | # along with this program; if not, write to the Free Software |
|---|
| 20 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|---|
| 21 | # |
|---|
| 22 | # requires pybluez - http://org.csail.mit.edu/pybluez/ |
|---|
| 23 | |
|---|
| 24 | import bluetooth |
|---|
| 25 | import os |
|---|
| 26 | import sys |
|---|
| 27 | import math |
|---|
| 28 | import time |
|---|
| 29 | import fcntl,struct |
|---|
| 30 | from time import strftime,localtime |
|---|
| 31 | from logging import debug |
|---|
| 32 | |
|---|
| 33 | def i2bs(x): |
|---|
| 34 | '''Convert a (32-bit) int to a list of 4 byte values, e.g. |
|---|
| 35 | i2bs(0xdeadbeef) = [222,173,190,239] |
|---|
| 36 | 12bs(0x4) = [0,0,0,4]''' |
|---|
| 37 | out=[] |
|---|
| 38 | while x or len(out) < 4: |
|---|
| 39 | out = [x & 0xff] + out |
|---|
| 40 | x = x >> 8 |
|---|
| 41 | return out |
|---|
| 42 | |
|---|
| 43 | class WiiDiscoverer(bluetooth.DeviceDiscoverer): |
|---|
| 44 | def __init__(self,maxdevs=1): |
|---|
| 45 | bluetooth.DeviceDiscoverer.__init__(self) # init parent |
|---|
| 46 | self.wiimotes = [] |
|---|
| 47 | self.done = False |
|---|
| 48 | self.inprogress = False |
|---|
| 49 | self.maxdevs = maxdevs |
|---|
| 50 | |
|---|
| 51 | # We identify wiimotes by their device name at the moment |
|---|
| 52 | def device_discovered(self, address,device_class, name): |
|---|
| 53 | if not name: |
|---|
| 54 | name = bluetooth.lookup_name(address) |
|---|
| 55 | |
|---|
| 56 | if name.startswith('Nintendo RVL-CNT'): |
|---|
| 57 | debug("Found wiimote at address %s" % address) |
|---|
| 58 | w=Wiimote(address,len(self.wiimotes)) |
|---|
| 59 | self.wiimotes.append(w) |
|---|
| 60 | if len(self.wiimotes) == self.maxdevs: |
|---|
| 61 | self.done = True |
|---|
| 62 | |
|---|
| 63 | def pre_inquiry(self): |
|---|
| 64 | self.inprogress = True |
|---|
| 65 | |
|---|
| 66 | def inquiry_complete(self): |
|---|
| 67 | self.inprogress = False |
|---|
| 68 | self.done = True |
|---|
| 69 | |
|---|
| 70 | buttonmap = { |
|---|
| 71 | '2': 0x0001, |
|---|
| 72 | '1': 0x0002, |
|---|
| 73 | 'B': 0x0004, |
|---|
| 74 | 'A': 0x0008, |
|---|
| 75 | '-': 0x0010, |
|---|
| 76 | 'H': 0x0080, |
|---|
| 77 | 'L': 0x0100, |
|---|
| 78 | 'R': 0x0200, |
|---|
| 79 | 'D': 0x0400, |
|---|
| 80 | 'U': 0x0800, |
|---|
| 81 | '+': 0x1000, |
|---|
| 82 | } |
|---|
| 83 | |
|---|
| 84 | # BLUH. These should be less C-ish. |
|---|
| 85 | CMD_SET_REPORT = 0x52 |
|---|
| 86 | RID_LEDS = 0x11 |
|---|
| 87 | RID_MODE = 0x12 |
|---|
| 88 | RID_IR_EN = 0x13 |
|---|
| 89 | RID_SPK_EN = 0x14 |
|---|
| 90 | RID_STATUS = 0x15 |
|---|
| 91 | RID_WMEM = 0x16 |
|---|
| 92 | RID_RMEM = 0x17 |
|---|
| 93 | RID_SPK = 0x18 |
|---|
| 94 | RID_SPK_MUTE = 0x19 |
|---|
| 95 | RID_IR_EN2 = 0x1a |
|---|
| 96 | MODE_BASIC = 0x30 |
|---|
| 97 | MODE_ACC = 0x31 |
|---|
| 98 | MODE_IR = 0x32 |
|---|
| 99 | MODE_FULL = 0x3e |
|---|
| 100 | IR_MODE_OFF = 0 |
|---|
| 101 | IR_MODE_STD = 1 |
|---|
| 102 | IR_MODE_EXP = 3 |
|---|
| 103 | IR_MODE_FULL = 5 |
|---|
| 104 | FEATURE_DISABLE = 0x00 |
|---|
| 105 | FEATURE_ENABLE = 0x04 |
|---|
| 106 | |
|---|
| 107 | # Max value for IR dots |
|---|
| 108 | DOT_MAX = 0x3ff |
|---|
| 109 | |
|---|
| 110 | def rotate(x,y,theta): |
|---|
| 111 | '''Rotates the given (x,y) coordinates by theta radians around the center |
|---|
| 112 | of the dots' view''' |
|---|
| 113 | |
|---|
| 114 | # Translate dot values so the center is (0,0) |
|---|
| 115 | c=(DOT_MAX/2) |
|---|
| 116 | x = c - x |
|---|
| 117 | y = c - y |
|---|
| 118 | |
|---|
| 119 | # rotate about the center |
|---|
| 120 | xprime = x*math.cos(theta) - y*math.sin(theta) |
|---|
| 121 | yprime = x*math.sin(theta) + y*math.cos(theta) |
|---|
| 122 | |
|---|
| 123 | # now retranslate |
|---|
| 124 | xprime = xprime + c |
|---|
| 125 | yprime = yprime + c |
|---|
| 126 | |
|---|
| 127 | return (int(xprime),int(yprime)) |
|---|
| 128 | |
|---|
| 129 | class Wiimote(object): |
|---|
| 130 | def __init__(self,addr,number=0): |
|---|
| 131 | self.connected=False |
|---|
| 132 | self.done=False |
|---|
| 133 | self.addr=addr |
|---|
| 134 | self.number=number |
|---|
| 135 | self.mode = 0 |
|---|
| 136 | self.ledmask = 0 |
|---|
| 137 | self.buttonmask = 0 |
|---|
| 138 | self.force = [0,0,0] |
|---|
| 139 | self.force_zero = [0,0,0] |
|---|
| 140 | self.force_1g = [0,0,0] |
|---|
| 141 | self.force_1g_diff = [0,0,0] # Difference between zero and 1g |
|---|
| 142 | self.theta_g = 0.0 # Angle of the remote with respect to gravity, |
|---|
| 143 | # calculated from the z-axis force. In radians. |
|---|
| 144 | |
|---|
| 145 | self.theta_g_x = 0.0 # Same, but calculated from x-axis. |
|---|
| 146 | self.dots = [(DOT_MAX,DOT_MAX),(DOT_MAX,DOT_MAX)] |
|---|
| 147 | self.theta = 0.0 # dots' angle (again, in rad) from horizontal |
|---|
| 148 | self.dotlist = [] # a fifo queue of recent dots |
|---|
| 149 | self.maxdots = 10 # max length for dotlist |
|---|
| 150 | self.pointer = [0,0] # Location of pointer. range is (0,DOT_MAX) |
|---|
| 151 | self.rx = bluetooth.BluetoothSocket(bluetooth.L2CAP) |
|---|
| 152 | self.cx = bluetooth.BluetoothSocket(bluetooth.L2CAP) |
|---|
| 153 | |
|---|
| 154 | def connect(self): |
|---|
| 155 | debug("Attaching to Wiimote #%i at %s", self.number+1, self.addr) |
|---|
| 156 | self.rx.connect((self.addr,19)) |
|---|
| 157 | self.cx.connect((self.addr,17)) |
|---|
| 158 | self.setled(self.number) |
|---|
| 159 | self.connected=True |
|---|
| 160 | |
|---|
| 161 | def disconnect(self): |
|---|
| 162 | debug("Disconnecting from Wiimote #%i", self.number+1) |
|---|
| 163 | self.cx.close() |
|---|
| 164 | self.rx.close() |
|---|
| 165 | self.connected=False |
|---|
| 166 | |
|---|
| 167 | def mainloop(self): |
|---|
| 168 | debug("Receiving data from Wiimote #%i", self.number+1) |
|---|
| 169 | |
|---|
| 170 | while not self.done: |
|---|
| 171 | self._getpacket() |
|---|
| 172 | debug("time %f force= %s %s ", time.time(),self.force_str(),self.buttons_str()) |
|---|
| 173 | |
|---|
| 174 | def _handle_button_data(self,data): |
|---|
| 175 | if len(data) != 4: |
|---|
| 176 | return False |
|---|
| 177 | |
|---|
| 178 | # XXX: what's byte 1 for? |
|---|
| 179 | newmask = (ord(data[2])<<8) + ord(data[3]) |
|---|
| 180 | |
|---|
| 181 | # TODO: check newmask against current mask and send events? |
|---|
| 182 | if newmask & buttonmap['H'] and not self.buttonmask & buttonmap['H']: |
|---|
| 183 | debug("Re-enabling IR") |
|---|
| 184 | self.enable_IR() |
|---|
| 185 | |
|---|
| 186 | self.buttonmask = newmask |
|---|
| 187 | |
|---|
| 188 | def _handle_force_data(self,data): |
|---|
| 189 | if len(data) != 3: |
|---|
| 190 | return False |
|---|
| 191 | |
|---|
| 192 | self.force = [ord(d) for d in data] |
|---|
| 193 | return True |
|---|
| 194 | |
|---|
| 195 | def _handle_IR_data(self,data): |
|---|
| 196 | if len(data) != 6: |
|---|
| 197 | return False |
|---|
| 198 | |
|---|
| 199 | if data ==' \xff' * 6: |
|---|
| 200 | self.dots=[(DOT_MAX,DOT_MAX),(DOT_MAX,DOT_MAX)] |
|---|
| 201 | else: |
|---|
| 202 | a,b,c,d,e,f = [ord(d) for d in data] |
|---|
| 203 | # processing dots: |
|---|
| 204 | # each tuple is 3 bytes in the form: x,y,extra |
|---|
| 205 | # extra contains 8 bits of extra data as follows: [yyxxssss] |
|---|
| 206 | # x and y are the high two bits for the full 10-bit x/y values. |
|---|
| 207 | # s is some unknown info (size data?) |
|---|
| 208 | x1=a+((c & 0x30) << 4) |
|---|
| 209 | y1=b+((c & 0xc0) << 2) |
|---|
| 210 | x2=d+((f & 0x30) << 4) |
|---|
| 211 | y2=e+((f & 0xc0) << 2) |
|---|
| 212 | self.dots=[(x1,y1),(x2,y2)] |
|---|
| 213 | self.dotlist.insert(0,self.dots) |
|---|
| 214 | |
|---|
| 215 | if len(self.dotlist) > self.maxdots: |
|---|
| 216 | self.dotlist.pop() |
|---|
| 217 | return True |
|---|
| 218 | |
|---|
| 219 | def _getpacket(self): |
|---|
| 220 | data=self.rx.recv(1024) |
|---|
| 221 | |
|---|
| 222 | if len(data) == 4: # button |
|---|
| 223 | self._handle_button_data(data) |
|---|
| 224 | |
|---|
| 225 | elif len(data) == 7: # button + accelerometer |
|---|
| 226 | self._handle_button_data(data[0:4]) |
|---|
| 227 | self._handle_force_data(data[4:7]) |
|---|
| 228 | |
|---|
| 229 | elif len(data) == 19: # button + accel + IR |
|---|
| 230 | self._handle_button_data(data[0:4]) |
|---|
| 231 | self._handle_force_data(data[4:7]) |
|---|
| 232 | self._handle_IR_data(data[7:13]) |
|---|
| 233 | # I think the extra data is emitted if we see more than two dots |
|---|
| 234 | extradata = data[13:19] |
|---|
| 235 | if extradata != "\xff"*len(extradata): |
|---|
| 236 | debug("Interesting extradata: %s\n", extradata.encode("hex")) |
|---|
| 237 | |
|---|
| 238 | elif len(data) == 0: # Wiimote went away! |
|---|
| 239 | debug("Lost wiimote #%i", self.number+1) |
|---|
| 240 | self.done = True |
|---|
| 241 | else: |
|---|
| 242 | debug("Unknown packet len %i: 0x%s" , len(data), data.encode("hex")) |
|---|
| 243 | |
|---|
| 244 | def setled(self, num): |
|---|
| 245 | debug("setled(%i)", num) |
|---|
| 246 | |
|---|
| 247 | if num < 4: |
|---|
| 248 | self.ledmask = self.ledmask | (0x10 << num) |
|---|
| 249 | self._led_command() |
|---|
| 250 | |
|---|
| 251 | def clearled(self,num): |
|---|
| 252 | debug("clearled(%i)", num) |
|---|
| 253 | |
|---|
| 254 | if num < 4: |
|---|
| 255 | self.ledmask = self.ledmask & ~(0x10 << num) |
|---|
| 256 | self._led_command() |
|---|
| 257 | |
|---|
| 258 | def buttons_str(self): |
|---|
| 259 | buttonlist='+UDLRH-AB12' |
|---|
| 260 | out='' |
|---|
| 261 | for c in buttonlist: |
|---|
| 262 | if not self.buttonmask & buttonmap[c]: |
|---|
| 263 | c = '.' |
|---|
| 264 | out = out + c |
|---|
| 265 | return out |
|---|
| 266 | |
|---|
| 267 | def force_gx(self): |
|---|
| 268 | return (self.force[0]-self.force_zero[0]) |
|---|
| 269 | |
|---|
| 270 | def force_gy(self): |
|---|
| 271 | return (self.force[1]-self.force_zero[1]) |
|---|
| 272 | |
|---|
| 273 | def force_gz(self): |
|---|
| 274 | return (self.force[2]-self.force_zero[2]) |
|---|
| 275 | |
|---|
| 276 | def force_str(self): |
|---|
| 277 | return "% 4i,% 4i,% 4i" % (self.force_gx(), self.force_gy(), self.force_gz()) |
|---|
| 278 | |
|---|
| 279 | def dots_str(self): |
|---|
| 280 | (a,b),(c,d) = self.dots |
|---|
| 281 | return "((%4i,%4i),(%4i,%4i))" % (a,b,c,d) |
|---|
| 282 | |
|---|
| 283 | def setmode(self,mode): |
|---|
| 284 | self.mode = mode |
|---|
| 285 | # XXX wiimotulator.py has flags for setting 0x01 in the first byte for |
|---|
| 286 | # 'rmbl' and 0x04 for 'cont'. Both of these are always off. |
|---|
| 287 | # No idea why. |
|---|
| 288 | self._send_command(CMD_SET_REPORT,RID_MODE,[0,mode]) |
|---|
| 289 | |
|---|
| 290 | def enable_force(self): |
|---|
| 291 | self.setmode(self.mode | MODE_ACC) |
|---|
| 292 | self.get_force_calibration() |
|---|
| 293 | |
|---|
| 294 | def enable_IR(self): |
|---|
| 295 | self.setmode(self.mode | MODE_IR) |
|---|
| 296 | self._send_command(CMD_SET_REPORT,RID_IR_EN,[FEATURE_ENABLE]) |
|---|
| 297 | self._send_command(CMD_SET_REPORT,RID_IR_EN2,[FEATURE_ENABLE]) |
|---|
| 298 | # Enable IR device |
|---|
| 299 | self._write_mem(0x04b00030,[0x01]) |
|---|
| 300 | # Set sensitivity constants |
|---|
| 301 | self._write_mem(0x04b00030,[0x08]) |
|---|
| 302 | self._write_mem(0x04b00006,[0x90]) |
|---|
| 303 | self._write_mem(0x04b00008,[0xc0]) |
|---|
| 304 | self._write_mem(0x04b0001a,[0x40]) |
|---|
| 305 | self._write_mem(0x04b00033,[0x33]) |
|---|
| 306 | # Enable IR data output |
|---|
| 307 | self._write_mem(0x04b00030,[8]) |
|---|
| 308 | |
|---|
| 309 | def get_force_calibration(self): |
|---|
| 310 | data=[ord(b) for b in self._read_mem(0x16,10)] |
|---|
| 311 | self.force_zero = data[0:3] |
|---|
| 312 | self.force_1g = data[4:7] |
|---|
| 313 | # XXX currently we don't know what data[3], data[7], or data[8:9] are |
|---|
| 314 | debug("Got force calibration data: zero=%s, 1g=%s", self.force_zero, self.force_1g) |
|---|
| 315 | # Calculate the difference between zero and 1g for each axis |
|---|
| 316 | for b in range(0,3): |
|---|
| 317 | self.force_1g_diff[b] = self.force_1g[b] - self.force_zero[b] |
|---|
| 318 | |
|---|
| 319 | def _led_command(self): |
|---|
| 320 | self._send_command(CMD_SET_REPORT,RID_LEDS,[self.ledmask]) |
|---|
| 321 | |
|---|
| 322 | def _waitforpacket(self,header,max=32): |
|---|
| 323 | r='' |
|---|
| 324 | n=0 |
|---|
| 325 | while (n<max) and not r.startswith(header): |
|---|
| 326 | r = self.rx.recv(1024) |
|---|
| 327 | n = n + 1 |
|---|
| 328 | |
|---|
| 329 | debug("Leaving _waitforpacket() after %i packets", n) |
|---|
| 330 | |
|---|
| 331 | if not r.startswith(header): |
|---|
| 332 | return None |
|---|
| 333 | else: |
|---|
| 334 | return r |
|---|
| 335 | |
|---|
| 336 | def _waitforok(self): |
|---|
| 337 | self._waitforpacket('\xa1\x22\x00') |
|---|
| 338 | |
|---|
| 339 | def _read_mem(self,offset,size): |
|---|
| 340 | if size >= 16: |
|---|
| 341 | debug("ERROR: _read_mem can't handle size > 15 yet") |
|---|
| 342 | return None |
|---|
| 343 | |
|---|
| 344 | # RMEM command wants: [offset,size] |
|---|
| 345 | self._send_command(CMD_SET_REPORT,RID_RMEM,i2bs(offset)+[0,size]) |
|---|
| 346 | data = self._waitforpacket('\xa1\x21') |
|---|
| 347 | if data: |
|---|
| 348 | # TODO check error flag, continuation, etc |
|---|
| 349 | return data[7:] |
|---|
| 350 | else: |
|---|
| 351 | return None |
|---|
| 352 | |
|---|
| 353 | def _write_mem(self,offset,data): |
|---|
| 354 | # WMEM command wants: [offset,size,data] |
|---|
| 355 | # offset = 32-bit, bigendian. data is 0-padded to 16 bytes. |
|---|
| 356 | size = len(data) |
|---|
| 357 | if size > 16: |
|---|
| 358 | return False # Too much data! |
|---|
| 359 | |
|---|
| 360 | if size < 16: |
|---|
| 361 | data = data + [0]*(16-size) |
|---|
| 362 | |
|---|
| 363 | self._send_command(CMD_SET_REPORT,RID_WMEM,i2bs(offset)+[size]+data) |
|---|
| 364 | self._waitforok() |
|---|
| 365 | |
|---|
| 366 | def _send_command(self,cmd,report,data): |
|---|
| 367 | debug("_send_command(%#x,%#x,%s)", cmd, report, data) |
|---|
| 368 | self.cx.send(chr(cmd) + chr(report) + "".join([chr(d) for d in data])) |
|---|
| 369 | |
|---|
| 370 | def calc_theta_g(self): |
|---|
| 371 | '''Use the z and x accelerometer values to figure out the wiimote's |
|---|
| 372 | orientation with respect to gravity.''' |
|---|
| 373 | |
|---|
| 374 | # sanity - return if we have no calibration data |
|---|
| 375 | if self.force_1g[0] == 0: |
|---|
| 376 | return self.theta_g |
|---|
| 377 | |
|---|
| 378 | # rotating from face-up to upside-down, force[2] goes from |
|---|
| 379 | # force_1g[2] to force_zero[2]-force_1g[2]. The normal force of |
|---|
| 380 | # gravity should be force_zero-force_1g - call this 'g'. |
|---|
| 381 | # It seems intuitive that this should map to a cosine wave - we start |
|---|
| 382 | # at 1g for face-up, then zero for a quarter-turn, -1g for half, etc. |
|---|
| 383 | zg = float(self.force[2]-self.force_zero[2])/self.force_1g_diff[2] |
|---|
| 384 | # If we're seeing more than 1g, probably this data isn't reliable |
|---|
| 385 | # for determining orientation, so we ignore it |
|---|
| 386 | if abs(zg) <= 1.0: |
|---|
| 387 | self.theta_g = math.acos(zg) |
|---|
| 388 | # Do the same thing with force[0] - it goes from 0->+/-1g->0, just like |
|---|
| 389 | # a sine wave |
|---|
| 390 | xg = float(self.force[0]-self.force_zero[0])/self.force_1g_diff[0] |
|---|
| 391 | if abs(xg) <= 1.0: |
|---|
| 392 | self.theta_g_x = math.asin(xg) |
|---|
| 393 | # For convenience, return theta_g |
|---|
| 394 | return self.theta_g |
|---|
| 395 | |
|---|
| 396 | |
|---|
| 397 | def calc_pointer(self): |
|---|
| 398 | '''Calculate the position of the pointer, taking into account the |
|---|
| 399 | rotation of the controller. |
|---|
| 400 | Sets self.theta and self.pointer; returns self.pointer.''' |
|---|
| 401 | |
|---|
| 402 | # Credit for most of the math here goes to my esteemed colleague Mike |
|---|
| 403 | # (mikem@redhat.com). Finally, all those years TA-ing Calc 1 are |
|---|
| 404 | # paying off! |
|---|
| 405 | # One of the dots is bogus/missing. Bail out. |
|---|
| 406 | # TODO: keep track of the previous dot positions and guess instead of |
|---|
| 407 | # immediately bailing out? |
|---|
| 408 | if (DOT_MAX, DOT_MAX) in self.dots: |
|---|
| 409 | return self.pointer |
|---|
| 410 | |
|---|
| 411 | ((x1,y1),(x2,y2)) = self.dots |
|---|
| 412 | # FIXME: for some reason, py never goes above ~750. |
|---|
| 413 | # Might be my bogus IR emitters (half-power every 15 degrees |
|---|
| 414 | # away from center! Thanks, Radio Shack.) |
|---|
| 415 | # But it might also be that the IR camera is calibrated to |
|---|
| 416 | # assume the sensor bar should be on the bottom of the TV. |
|---|
| 417 | # Since IR calibration is still Black Magick, I am forcing |
|---|
| 418 | # a scale factor for y here. |
|---|
| 419 | y1 = y1 * DOT_MAX / 760 |
|---|
| 420 | y2 = y2 * DOT_MAX / 760 |
|---|
| 421 | # Determine rotation angle. SOH CAH TOA ftw. |
|---|
| 422 | if (x1 != x2): |
|---|
| 423 | self.theta = math.atan(float(y2-y1)/float(x1-x2)) |
|---|
| 424 | else: |
|---|
| 425 | self.theta = math.pi/2 |
|---|
| 426 | if y1 > y2: |
|---|
| 427 | self.theta = -self.theta |
|---|
| 428 | |
|---|
| 429 | # If the accel. says we are upside-down, add half a turn to theta |
|---|
| 430 | tg = math.degrees(self.calc_theta_g()) |
|---|
| 431 | if tg > 90.0: |
|---|
| 432 | self.theta = self.theta+math.pi |
|---|
| 433 | if tg < -90.0: |
|---|
| 434 | self.theta = self.theta-math.pi |
|---|
| 435 | # rotate dots around center by theta. |
|---|
| 436 | (x1,y1) = rotate(x1,y1,self.theta) |
|---|
| 437 | (x2,y2) = rotate(x2,y2,self.theta) |
|---|
| 438 | # They should now be horizontal (y1 should be very close to y2). |
|---|
| 439 | # Average the two X values (find the center between them) |
|---|
| 440 | px = (x1+x2)/2 |
|---|
| 441 | # Horizontal means y1 = y2, so there's no need to average them. |
|---|
| 442 | # In fact, let's output an error message if the rotate messed up. |
|---|
| 443 | if y2 != y1: |
|---|
| 444 | debug("post-rotation Y delta=%i", abs(y1-y2)) |
|---|
| 445 | # We do need to flip the incoming y data. |
|---|
| 446 | py = DOT_MAX - y1 |
|---|
| 447 | # Do some scaling - ignore the outer edges of the screen |
|---|
| 448 | # FIXME: fix scaling such that the center of the wiimote image |
|---|
| 449 | # maps to the top of the screen |
|---|
| 450 | # Center point of the screen is (c,c) |
|---|
| 451 | c = DOT_MAX/2 |
|---|
| 452 | maxd = 0.33 * DOT_MAX # max allowable distance from center |
|---|
| 453 | # If this point is less than (maxd) from the center of the image, |
|---|
| 454 | # draw it. |
|---|
| 455 | if (abs(px-c) <= maxd) and (abs(py-c) <= maxd): |
|---|
| 456 | # px/py are in the range [c-maxd,c+maxd] |
|---|
| 457 | px = px - (c-maxd) |
|---|
| 458 | py = py - (c-maxd) |
|---|
| 459 | # Now they're in the range [0,2*maxd]. Scale to DOT_MAX. |
|---|
| 460 | px = px * (DOT_MAX/(2*maxd)) |
|---|
| 461 | py = py * (DOT_MAX/(2*maxd)) |
|---|
| 462 | # Hooray! We did it! |
|---|
| 463 | self.pointer = [int(px),int(py)] |
|---|
| 464 | return self.pointer |
|---|
| 465 | |
|---|
| 466 | def pointer_str(self): |
|---|
| 467 | return "(%4i,%4i)" % (self.pointer[0],self.pointer[1]) |
|---|
| 468 | |
|---|
| 469 | |
|---|
| 470 | if __name__ == '__main__': |
|---|
| 471 | import logging |
|---|
| 472 | |
|---|
| 473 | logging.basicConfig(level=logging.DEBUG, |
|---|
| 474 | format='%(message)s') |
|---|
| 475 | |
|---|
| 476 | w=Wiimote("00:19:1D:BB:7D:28",0) |
|---|
| 477 | w.connect() |
|---|
| 478 | print "Enabling accelerometer." |
|---|
| 479 | w.enable_force() |
|---|
| 480 | #w.enable_IR() |
|---|
| 481 | try: |
|---|
| 482 | last=time.time() |
|---|
| 483 | while not w.done: |
|---|
| 484 | w._getpacket() |
|---|
| 485 | debug("time %f force= %s %s ", time.time(), w.force_str(), w.buttons_str()) |
|---|
| 486 | finally: |
|---|
| 487 | w.disconnect() |
|---|
| 488 | |
|---|