2 // Copyright by Masayuki Akamatsu
3 // Based on "DarwiinRemote" by Hiroaki Kimura
9 // this type is used a lot (data array):
10 typedef unsigned char darr
[];
14 // the unit of kWait is microseconds, thus 10000 means 10ms
16 #define kWiiIRPixelsWidth 1024.0
17 #define kWiiIRPixelsHeight 768.0
20 Boolean
requestUpdates(WiiRemoteRef wiiremote
);
21 void myEventListener(IOBluetoothL2CAPChannelRef channel
, void *refCon
, IOBluetoothL2CAPChannelEvent
*event
);
24 //--------------------------------------------------------------------------------------------
25 //--------------------------------------------------------------------------------------------
27 void wiiremote_init(WiiRemoteRef wiiremote
)
29 wiiremote
->inquiry
= nil
;
30 wiiremote
->device
= nil
;
31 wiiremote
->ichan
= nil
;
32 wiiremote
->cchan
= nil
;
34 wiiremote
->address
= nil
;
36 wiiremote
->accX
= 0x10;
37 wiiremote
->accY
= 0x10;
38 wiiremote
->accZ
= 0x10;
39 wiiremote
->buttonData
= 0;
43 wiiremote
->leftPoint
= -1;
44 wiiremote
->tracking
= false;
46 wiiremote
->batteryLevel
= 0;
48 wiiremote
->readingRegister
= false;
49 wiiremote
->isMotionSensorEnabled
= false;
50 wiiremote
->isVibrationEnabled
= false;
51 wiiremote
->isIRSensorEnabled
= false;
52 wiiremote
->wiiIRMode
= kWiiIRModeExtended
;
53 wiiremote
->isExpansionPortEnabled
= false;
54 wiiremote
->isExpansionPortAttached
= false;
55 wiiremote
->expType
= WiiExpNotAttached
;
57 wiiremote
->isLED1Illuminated
= false;
58 wiiremote
->isLED2Illuminated
= false;
59 wiiremote
->isLED3Illuminated
= false;
60 wiiremote
->isLED4Illuminated
= false;
62 wiiremote
->nAccX
= 0x10;
63 wiiremote
->nAccY
= 0x10;
64 wiiremote
->nAccZ
= 0x10;
65 wiiremote
->nButtonData
= 0;
72 //--------------------------------------------------------------------------------------------
73 //--------------------------------------------------------------------------------------------
75 Boolean
openCChan(WiiRemoteRef wiiremote
)
80 // open L2CAPChannel : BluetoothL2CAPPSM = 17
81 for (i
=0; i
<kTrial
; i
++)
83 ret
= IOBluetoothDeviceOpenL2CAPChannelSync(wiiremote
->device
, &(wiiremote
->cchan
), 17, myEventListener
, (void *)wiiremote
);
84 if ( ret
== kIOReturnSuccess
)
86 usleep(kWait
); // wait 10ms
90 wiiremote
->cchan
= nil
;
91 IOBluetoothDeviceCloseConnection(wiiremote
->device
);
94 IOBluetoothObjectRetain(wiiremote
->cchan
);
96 return (ret
==kIOReturnSuccess
);
99 Boolean
openIChan(WiiRemoteRef wiiremote
)
104 // open L2CAPChannel : BluetoothL2CAPPSM = 19
105 for (i
=0; i
<kTrial
; i
++)
107 ret
= IOBluetoothDeviceOpenL2CAPChannelSync(wiiremote
->device
, &(wiiremote
->ichan
), 19, myEventListener
, (void *)wiiremote
);
108 if ( ret
== kIOReturnSuccess
)
110 usleep(kWait
); // wait 10ms
114 wiiremote
->ichan
= nil
;
115 IOBluetoothL2CAPChannelCloseChannel(wiiremote
->cchan
);
116 IOBluetoothObjectRelease(wiiremote
->cchan
);
117 IOBluetoothDeviceCloseConnection(wiiremote
->device
);
120 IOBluetoothObjectRetain(wiiremote
->ichan
);
122 return (ret
==kIOReturnSuccess
);
125 //--------------------------------------------------------------------------------------------
127 Boolean
sendCommand(WiiRemoteRef wiiremote
, unsigned char *data
, unsigned short length
)
129 unsigned char buf
[40];
135 memcpy(buf
+1, data
, length
);
141 usleep(kWait
); // wait 10ms // Done to make sure commands don't happen too fast.
143 for (i
= 0; i
<kTrial
; i
++)
145 ret
= IOBluetoothL2CAPChannelWriteSync(wiiremote
->cchan
, buf
, length
);
146 if (ret
== kIOReturnSuccess
)
151 if (ret
!= kIOReturnSuccess
)
152 wiiremote_disconnect(wiiremote
);
154 return (ret
==kIOReturnSuccess
);
157 Boolean
writeData(WiiRemoteRef wiiremote
, const unsigned char *data
, unsigned long address
, unsigned short length
)
159 unsigned char cmd
[22];
161 unsigned long addr
= address
;
164 for(i
=0 ; i
<length
; i
++)
171 cmd
[1] = (addr
>>24) & 0xFF;
172 cmd
[2] = (addr
>>16) & 0xFF;
173 cmd
[3] = (addr
>> 8) & 0xFF;
174 cmd
[4] = (addr
>> 0) & 0xFF;
177 // and of course the vibration flag, as usual
178 if (wiiremote
->isVibrationEnabled
) cmd
[1] |= 0x01;
182 return sendCommand(wiiremote
, cmd
, 22);
185 Boolean
readData(WiiRemoteRef wiiremote
, unsigned long address
, unsigned short length
)
188 unsigned char cmd
[7];
189 unsigned long addr
= address
;
190 unsigned short len
= length
;
193 cmd
[1] = (addr
>>24)&0xFF;
194 cmd
[2] = (addr
>>16)&0xFF;
195 cmd
[3] = (addr
>> 8)&0xFF;
196 cmd
[4] = (addr
>> 0)&0xFF;
198 cmd
[5] = (len
>> 8)&0xFF;
199 cmd
[6] = (len
>> 0)&0xFF;
201 if (wiiremote
->isVibrationEnabled
) cmd
[1] |= 0x01;
202 if (cmd
[1] & 0x02) wiiremote
->readingRegister
= true;
204 return sendCommand(wiiremote
, cmd
, 7);
207 //--------------------------------------------------------------------------------------------
208 //--------------------------------------------------------------------------------------------
210 void checkDevice(WiiRemoteRef wiiremote
, IOBluetoothDeviceRef device
)
215 if (wiiremote_isconnected(wiiremote
))
218 name
= IOBluetoothDeviceGetName(device
);
219 address
= IOBluetoothDeviceGetAddressString(device
);
220 if (name
!= nil
&& address
!= nil
)
222 if (CFStringCompare(name
, CFSTR("Nintendo RVL-CNT-01"), 0) == kCFCompareEqualTo
)
224 if ( CFStringGetLength(wiiremote
->address
) == 0
225 || CFStringCompare(address
, wiiremote
->address
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
)
227 wiiremote
->device
= IOBluetoothObjectRetain(device
);
228 if ( wiiremote_connect(wiiremote
) == false )
229 wiiremote_disconnect(wiiremote
);
235 void myFoundFunc(void *refCon
, IOBluetoothDeviceInquiryRef inquiry
, IOBluetoothDeviceRef device
)
237 checkDevice((WiiRemoteRef
)refCon
, device
);
240 void myUpdatedFunc(void *refCon
, IOBluetoothDeviceInquiryRef inquiry
, IOBluetoothDeviceRef device
, uint32_t devicesRemaining
)
242 checkDevice((WiiRemoteRef
)refCon
, device
);
245 void myCompleteFunc(void *refCon
, IOBluetoothDeviceInquiryRef inquiry
, IOReturn error
, Boolean aborted
)
249 if (aborted
) return; // called by stop ;)
251 if (error
!= kIOReturnSuccess
)
253 wiiremote_stopsearch((WiiRemoteRef
)refCon
);
258 ret = IOBluetoothDeviceInquiryStart(((WiiRemoteRef)refCon)->inquiry);
259 if (ret != kIOReturnSuccess)
261 wiiremote_stopsearch((WiiRemoteRef)refCon);
266 //--------------------------------------------------------------------------------------------
268 Boolean
wiiremote_isconnected(WiiRemoteRef wiiremote
)
272 result
= wiiremote
->device
!= nil
&& IOBluetoothDeviceIsConnected(wiiremote
->device
);
276 Boolean
wiiremote_search(WiiRemoteRef wiiremote
, char *address
)
280 if (wiiremote
->inquiry
!= nil
)
283 wiiremote
->inquiry
= IOBluetoothDeviceInquiryCreateWithCallbackRefCon((void *)wiiremote
);
284 IOBluetoothDeviceInquirySetDeviceFoundCallback(wiiremote
->inquiry
, myFoundFunc
);
285 IOBluetoothDeviceInquirySetDeviceNameUpdatedCallback(wiiremote
->inquiry
, myUpdatedFunc
);
286 IOBluetoothDeviceInquirySetCompleteCallback(wiiremote
->inquiry
, myCompleteFunc
);
288 if (wiiremote
->address
!= nil
)
289 CFRelease(wiiremote
->address
);
290 wiiremote
->address
= CFStringCreateWithCString(nil
, address
, kCFStringEncodingMacRoman
);
292 ret
= IOBluetoothDeviceInquiryStart(wiiremote
->inquiry
);
293 if (ret
!= kIOReturnSuccess
)
295 IOBluetoothDeviceInquiryDelete(wiiremote
->inquiry
);
296 wiiremote
->inquiry
= nil
;
302 Boolean
wiiremote_stopsearch(WiiRemoteRef wiiremote
)
306 if (wiiremote
->inquiry
== nil
)
308 return true; // already stopped
311 ret
= IOBluetoothDeviceInquiryStop(wiiremote
->inquiry
);
313 if (ret
!= kIOReturnSuccess
&& ret
!= kIOReturnNotPermitted
)
315 // kIOReturnNotPermitted is if it's already stopped
318 IOBluetoothDeviceInquiryDelete(wiiremote
->inquiry
);
319 wiiremote
->inquiry
= nil
;
321 return (ret
==kIOReturnSuccess
);
324 //--------------------------------------------------------------------------------------------
325 //--------------------------------------------------------------------------------------------
327 unsigned char decrypt(unsigned char data
)
329 return (data
^ 0x17) + 0x17;
332 //--------------------------------------------------------------------------------------------
335 * Handle report 0x21 (Read Data) from wiimote.
336 * dp[0] = Bluetooth header
337 * dp[1] = (0x21) Report/Channel ID
338 * dp[2] = Wiimote Buttons
339 * dp[3] = Wiimote Buttons
340 * dp[4] = High 4 bits = payload size; Low 4 bits = Error flag (0 = all good)
341 * dp[5] = Offset of memory read
342 * dp[6] = Offset of memory read
346 void handleRAMData(WiiRemoteRef wiiremote
, unsigned char *dp
, size_t dataLength
)
348 // specify attached expasion device
349 if ((dp
[5] == 0x00) && (dp
[6] == 0xF0))
351 if (decrypt(dp
[21]) == 0x00)
353 wiiremote
->expType
= WiiNunchuk
;
356 if (decrypt(dp
[21]) == 0x01)
358 wiiremote
->expType
= WiiClassicController
;
362 wiiremote
->expType
= WiiExpNotAttached
;
368 // wiimote calibration data
369 if (!wiiremote
->readingRegister
&& dp
[5] == 0x00 && dp
[6] == 0x20)
371 wiiremote
->wiiCalibData
.accX_zero
= dp
[7];
372 wiiremote
->wiiCalibData
.accY_zero
= dp
[8];
373 wiiremote
->wiiCalibData
.accZ_zero
= dp
[9];
375 //dp[10] - unknown/unused
377 wiiremote
->wiiCalibData
.accX_1g
= dp
[11];
378 wiiremote
->wiiCalibData
.accY_1g
= dp
[12];
379 wiiremote
->wiiCalibData
.accZ_1g
= dp
[13];
383 // expansion device calibration data.
384 if (wiiremote
->readingRegister
&& dp
[5] == 0x00 && dp
[6] == 0x20)
386 if (wiiremote
->expType
== WiiNunchuk
)
388 //nunchuk calibration data
389 wiiremote
->nunchukCalibData
.accX_zero
= decrypt(dp
[7]);
390 wiiremote
->nunchukCalibData
.accY_zero
= decrypt(dp
[8]);
391 wiiremote
->nunchukCalibData
.accZ_zero
= decrypt(dp
[9]);
393 wiiremote
->nunchukCalibData
.accX_1g
= decrypt(dp
[11]);
394 wiiremote
->nunchukCalibData
.accY_1g
= decrypt(dp
[12]);
395 wiiremote
->nunchukCalibData
.accZ_1g
= decrypt(dp
[13]);
397 wiiremote
->nunchukJoyStickCalibData
.x_max
= decrypt(dp
[15]);
398 wiiremote
->nunchukJoyStickCalibData
.x_min
= decrypt(dp
[16]);
399 wiiremote
->nunchukJoyStickCalibData
.x_center
= decrypt(dp
[17]);
401 wiiremote
->nunchukJoyStickCalibData
.y_max
= decrypt(dp
[18]);
402 wiiremote
->nunchukJoyStickCalibData
.y_min
= decrypt(dp
[19]);
403 wiiremote
->nunchukJoyStickCalibData
.y_center
= decrypt(dp
[20]);
408 if (wiiremote
->expType
== WiiClassicController
)
410 //classic controller calibration data (probably)
414 // wii remote buttons
415 wiiremote
->buttonData
= ((short)dp
[2] << 8) + dp
[3];
418 void handleStatusReport(WiiRemoteRef wiiremote
, unsigned char *dp
, size_t dataLength
)
420 wiiremote
->batteryLevel
= (double)dp
[7];
421 wiiremote
->batteryLevel
/= (double)0xC0; // C0 = fully charged.
423 if ((dp
[4] & 0x02)) //some device attached to Wiimote
425 wiiremote
->isExpansionPortAttached
= true;
426 // initExpPort = YES;
428 Boolean ret
= writeData(wiiremote
, (darr
){0x00}, 0x04A40040, 1); // Initialize the device
432 wiiremote
->isExpansionPortAttached
= false;
436 usleep(kWait
); // Give the write a chance to be processed.
438 ret
= readData(wiiremote
, 0x04A400F0, 16); // read expansion device type
441 wiiremote
->isExpansionPortAttached
= false;
446 wiiremote
->isExpansionPortAttached
= false;
447 wiiremote
->expType
= WiiExpNotAttached
;
451 wiiremote
->isLED1Illuminated
= true;
453 wiiremote
->isLED1Illuminated
= false;
456 wiiremote
->isLED2Illuminated
= true;
458 wiiremote
->isLED2Illuminated
= false;
461 wiiremote
->isLED3Illuminated
= true;
463 wiiremote
->isLED3Illuminated
= false;
466 wiiremote
->isLED4Illuminated
= true;
468 wiiremote
->isLED4Illuminated
= false;
471 void handleExtensionData(WiiRemoteRef wiiremote
, unsigned char *dp
, size_t dataLength
)
473 unsigned char startByte
;
489 return; // This shouldn't ever happen.
493 if (wiiremote
->expType
== WiiNunchuk
)
495 wiiremote
->nStickX
= decrypt(dp
[startByte
]);
496 wiiremote
->nStickY
= decrypt(dp
[startByte
+1]);
497 wiiremote
->nAccX
= decrypt(dp
[startByte
+2]);
498 wiiremote
->nAccY
= decrypt(dp
[startByte
+3]);
499 wiiremote
->nAccZ
= decrypt(dp
[startByte
+4]);
500 wiiremote
->nButtonData
= decrypt(dp
[startByte
+5]);
502 wiiremote
->nLowZ
= wiiremote
->nLowZ
* .9 + wiiremote
->nAccZ
* .1;
503 wiiremote
->nLowX
= wiiremote
->nLowX
* .9 + wiiremote
->nAccX
* .1;
505 float absx
= abs(wiiremote
->nLowX
- 128);
506 float absz
= abs(wiiremote
->nLowZ
- 128);
508 if (wiiremote
->nOrientation
== 0 || wiiremote
->nOrientation
== 2) absx
-= 5;
509 if (wiiremote
->nOrientation
== 1 || wiiremote
->nOrientation
== 3) absz
-= 5;
514 wiiremote
->nOrientation
= (wiiremote
->nLowZ
> 128) ? 0 : 2;
519 wiiremote
->nOrientation
= (wiiremote
->nLowX
> 128) ? 3 : 1;
523 if (wiiremote
->expType
== WiiClassicController
)
525 wiiremote
->cButtonData
= (unsigned short)(decrypt(dp
[startByte
+ 4]) << 8) + decrypt(dp
[startByte
+ 5]);
526 wiiremote
->cButtonData
= ~wiiremote
->cButtonData
; // bit reverse
528 wiiremote
->cStickX1
= decrypt(dp
[startByte
]) & 0x3F;
529 wiiremote
->cStickY1
= decrypt(dp
[startByte
+ 1]) & 0x3F;
531 wiiremote
->cStickX2
= (((decrypt(dp
[startByte
+0]) & 0xC0) >> 3) |
532 ((decrypt(dp
[startByte
+1]) & 0xC0) >> 5) |
533 ((decrypt(dp
[startByte
+2]) & 0x80) >> 7)) & 0x1F;
534 wiiremote
->cStickY2
= decrypt(dp
[startByte
+ 2]) & 0x1F;
536 wiiremote
->cAnalogL
= (((decrypt(dp
[startByte
+2]) & 0x60) >> 2) |
537 ((decrypt(dp
[startByte
+3]) & 0xE0) >> 5)) & 0x1F;
538 wiiremote
->cAnalogR
= decrypt(dp
[startByte
+ 3]) & 0x1F;
542 void handleIRData(WiiRemoteRef wiiremote
, unsigned char *dp
, size_t dataLength
)
549 for(i
=0 ; i
< 4 ; i
++)
551 startByte
= 7 + 3 * i
;
552 wiiremote
->irData
[i
].x
= (dp
[startByte
+0] | ((dp
[startByte
+2] & 0x30) << 4)) & 0x3FF;
553 wiiremote
->irData
[i
].y
= (dp
[startByte
+1] | ((dp
[startByte
+2] & 0xC0) << 2)) & 0x3FF;
554 wiiremote
->irData
[i
].s
= dp
[startByte
+2] & 0x0F;
559 int shift
= (dp
[1] == 0x36) ? 4 : 7;
561 for (i
=0; i
< 2; i
++) {
562 startByte
= shift
+ 5 * i
;
563 wiiremote
->irData
[2*i
].x
= (dp
[startByte
+0] | ((dp
[startByte
+2] & 0x30) << 4)) & 0x3FF;
564 wiiremote
->irData
[2*i
].y
= (dp
[startByte
+1] | ((dp
[startByte
+2] & 0xC0) << 2)) & 0x3FF;
565 wiiremote
->irData
[2*i
].s
= ((wiiremote
->irData
[2*i
].x
== wiiremote
->irData
[2*i
].y
) && (wiiremote
->irData
[2*i
].x
== 0x3FF)) ? 0x0F : 0x05; // No size is given in 10 byte report.
567 wiiremote
->irData
[(2*i
)+1].x
= (dp
[startByte
+3] | ((dp
[startByte
+2] & 0x03) << 8)) & 0x3FF;
568 wiiremote
->irData
[(2*i
)+1].y
= (dp
[startByte
+4] | ((dp
[startByte
+2] & 0x0C) << 6)) & 0x3FF;
569 wiiremote
->irData
[(2*i
)+1].s
= ((wiiremote
->irData
[(2*i
)+1].x
== wiiremote
->irData
[(2*i
)+1].y
) && (wiiremote
->irData
[(2*i
)+1].x
== 0x3FF)) ? 0x0F : 0x05; // No size is given in 10 byte report.
575 // we should modify this loop to take the points with the lowest s (the brightest ones)
576 for (i
=0 ; i
<4 ; i
++) {
578 if (wiiremote
->irData
[i
].s
< 0x0F)
581 if (wiiremote
->irData
[i
].s
< 0x0F) {
589 if ((p1
> -1) && (p2
> -1))
591 int l
= wiiremote
->leftPoint
;
592 if (wiiremote
->leftPoint
== -1)
594 switch (wiiremote
->orientation
)
596 case 0: l
= (wiiremote
->irData
[p1
].x
< wiiremote
->irData
[p2
].x
) ? 0 : 1; break;
597 case 1: l
= (wiiremote
->irData
[p1
].y
> wiiremote
->irData
[p2
].y
) ? 0 : 1; break;
598 case 2: l
= (wiiremote
->irData
[p1
].x
> wiiremote
->irData
[p2
].x
) ? 0 : 1; break;
599 case 3: l
= (wiiremote
->irData
[p1
].y
< wiiremote
->irData
[p2
].y
) ? 0 : 1; break;
602 wiiremote
->leftPoint
= l
;
607 double dx
= wiiremote
->irData
[r
].x
- wiiremote
->irData
[l
].x
;
608 double dy
= wiiremote
->irData
[r
].y
- wiiremote
->irData
[l
].y
;
609 double d
= hypot (dx
, dy
);
614 double cx
= (wiiremote
->irData
[l
].x
+ wiiremote
->irData
[r
].x
)/kWiiIRPixelsWidth
- 1;
615 double cy
= (wiiremote
->irData
[l
].y
+ wiiremote
->irData
[r
].y
)/kWiiIRPixelsHeight
- 1;
621 // Compensate for distance. There must be fewer than 0.75*768 pixels between the spots for this to work.
622 // In other words, you have to be far enough away from the sensor bar for the two spots to have enough
623 // space on the image sensor to travel without one of the points going off the image.
624 // note: it is working very well ...
626 if (d
< (0.75 * kWiiIRPixelsHeight
))
627 gain
= 1 / (1 - d
/kWiiIRPixelsHeight
);
632 wiiremote
->angle
= atan2(dy
, dx
);
633 wiiremote
->tracking
= true;
638 wiiremote
->leftPoint
= -1; // not tracking
639 wiiremote
->angle
= -100;
640 wiiremote
->tracking
= false;
643 wiiremote
->posX
= ox
;
644 wiiremote
->posY
= oy
;
647 void handleButtonReport(WiiRemoteRef wiiremote
, unsigned char *dp
, size_t dataLength
)
650 wiiremote
->buttonData
= ((short)dp
[2] << 8) + dp
[3];
652 // report contains extension data
659 handleExtensionData(wiiremote
, dp
, dataLength
);
663 // report contains IR data
666 handleIRData(wiiremote
, dp
, dataLength
);
669 // report contains motion sensor data
672 wiiremote
->accX
= dp
[4];
673 wiiremote
->accY
= dp
[5];
674 wiiremote
->accZ
= dp
[6];
676 wiiremote
->lowZ
= wiiremote
->lowZ
* 0.9 + wiiremote
->accZ
* 0.1;
677 wiiremote
->lowX
= wiiremote
->lowX
* 0.9 + wiiremote
->accX
* 0.1;
679 float absx
= abs(wiiremote
->lowX
-128);
680 float absz
= abs(wiiremote
->lowZ
-128);
682 if (wiiremote
->orientation
== 0 || wiiremote
->orientation
== 2) absx
-= 5;
683 if (wiiremote
->orientation
== 1 || wiiremote
->orientation
== 3) absz
-= 5;
688 wiiremote
->orientation
= (wiiremote
->lowZ
> 128)?0:2;
693 wiiremote
->orientation
= (wiiremote
->lowX
> 128)?3:1;
698 //--------------------------------------------------------------------------------------------
700 void myDataListener(IOBluetoothL2CAPChannelRef channel
, void *dataPointer
, UInt16 dataLength
, void *refCon
)
702 WiiRemoteRef wiiremote
= (WiiRemoteRef
)refCon
;
703 unsigned char* dp
= (unsigned char*)dataPointer
;
705 if (!wiiremote
->device
)
708 //controller status (expansion port and battery level data) - received when report 0x15 sent to Wiimote (getCurrentStatus:) or status of expansion port changes.
709 if (dp
[1] == 0x20 && dataLength
>= 8)
711 handleStatusReport(wiiremote
, dp
, dataLength
);
712 requestUpdates(wiiremote
); // Make sure we keep getting state change reports.
718 handleRAMData(wiiremote
, dp
, dataLength
);
723 { // Write data response
724 //NSLog(@"Write data response: %00x %00x %00x %00x", dp[2], dp[3], dp[4], dp[5]);
728 // report contains button info
729 if ((dp
[1] & 0xF0) == 0x30)
731 handleButtonReport(wiiremote
, dp
, dataLength
);
735 void myEventListener(IOBluetoothL2CAPChannelRef channel
, void *refCon
, IOBluetoothL2CAPChannelEvent
*event
)
737 if (event
->eventType
== kIOBluetoothL2CAPChannelEventTypeData
)
740 // event->u.newData.dataPtr is a pointer to the block of data received.
741 // event->u.newData.dataSize is the size of the block of data.
742 myDataListener(channel
, event
->u
.data
.dataPtr
, event
->u
.data
.dataSize
, refCon
);
745 if (event
->eventType
== kIOBluetoothL2CAPChannelEventTypeClosed
)
748 // event->u.terminatedChannel is the channel that was terminated. It can be converted in an IOBluetoothL2CAPChannel
749 // object with [IOBluetoothL2CAPChannel withL2CAPChannelRef:]. (see below).
753 void myDisconnectedFunc(void * refCon
, IOBluetoothUserNotificationRef inRef
, IOBluetoothObjectRef objectRef
)
755 CFStringRef itsAddress
, myAddress
;
757 itsAddress
= IOBluetoothDeviceGetAddressString(objectRef
);
758 if (itsAddress
!= nil
)
760 myAddress
= IOBluetoothDeviceGetAddressString(((WiiRemoteRef
)refCon
)->device
);
761 if (myAddress
!= nil
)
763 if (CFStringCompare(itsAddress
, myAddress
, 0) == kCFCompareEqualTo
)
765 wiiremote_disconnect((WiiRemoteRef
)refCon
);
767 CFRelease(myAddress
);
769 CFRelease(itsAddress
);
773 //--------------------------------------------------------------------------------------------
775 void wiiremote_getaddress(WiiRemoteRef wiiremote
, char *address
)
777 CFStringRef cfstring
;
779 cfstring
= IOBluetoothDeviceGetAddressString(wiiremote
->device
);
780 CFStringGetCString(cfstring
, address
, 32, kCFStringEncodingMacRoman
);
785 //--------------------------------------------------------------------------------------------
787 Boolean
wiiremote_connect(WiiRemoteRef wiiremote
)
793 if (wiiremote
->device
== nil
)
796 // connect the device
797 for (i
=0; i
<kTrial
; i
++)
799 ret
= IOBluetoothDeviceOpenConnection(wiiremote
->device
, nil
, nil
);
800 if ( ret
== kIOReturnSuccess
)
802 usleep(kWait
); // wait 10ms
807 wiiremote
->disconnectNotification
= IOBluetoothDeviceRegisterForDisconnectNotification(wiiremote
->device
, myDisconnectedFunc
, (void *)wiiremote
);
809 // performs an SDP query
810 for (i
=0; i
<kTrial
; i
++)
812 ret
= IOBluetoothDevicePerformSDPQuery(wiiremote
->device
, nil
, nil
);
813 if ( ret
== kIOReturnSuccess
)
815 usleep(kWait
); // wait 10ms
820 result
= openCChan(wiiremote
);
821 result
= openIChan(wiiremote
);
825 result
= wiiremote_led(wiiremote
, wiiremote
->isLED1Illuminated
, wiiremote
->isLED2Illuminated
, wiiremote
->isLED3Illuminated
, wiiremote
->isLED4Illuminated
);
830 wiiremote_disconnect(wiiremote
);
834 wiiremote_getstatus(wiiremote
);
835 requestUpdates(wiiremote
);
837 readData(wiiremote
, 0x0020, 7); // Get Accelerometer callibration data
843 Boolean
wiiremote_disconnect(WiiRemoteRef wiiremote
)
847 if (wiiremote
->cchan
)
849 if (IOBluetoothDeviceIsConnected(wiiremote
->device
))
851 for (i
=0; i
<kTrial
; i
++)
853 if (IOBluetoothL2CAPChannelCloseChannel(wiiremote
->cchan
) == kIOReturnSuccess
)
855 usleep(kWait
); // wait 10ms
858 if (i
==kTrial
) return false;
859 IOBluetoothObjectRelease(wiiremote
->cchan
);
860 wiiremote
->cchan
= nil
;
863 if (wiiremote
->ichan
)
865 if (IOBluetoothDeviceIsConnected(wiiremote
->device
))
867 for (i
=0; i
<kTrial
; i
++)
869 if (IOBluetoothL2CAPChannelCloseChannel(wiiremote
->ichan
) == kIOReturnSuccess
)
873 if (i
==kTrial
) return false;
874 IOBluetoothObjectRelease(wiiremote
->ichan
);
875 wiiremote
->ichan
= nil
;
878 if (wiiremote
->device
)
880 if (IOBluetoothDeviceIsConnected(wiiremote
->device
))
882 for (i
=0; i
<kTrial
; i
++)
884 if (IOBluetoothDeviceCloseConnection(wiiremote
->device
) == kIOReturnSuccess
)
888 if (i
==kTrial
) return false;
889 IOBluetoothObjectRelease(wiiremote
->device
);
890 wiiremote
->device
= nil
;
893 if (wiiremote
->disconnectNotification
!= nil
)
895 IOBluetoothUserNotificationUnregister(wiiremote
->disconnectNotification
);
896 wiiremote
->disconnectNotification
= nil
;
902 //--------------------------------------------------------------------------------------------
903 //--------------------------------------------------------------------------------------------
905 Boolean
requestUpdates(WiiRemoteRef wiiremote
)
909 // Set the report type the Wiimote should send.
910 unsigned char cmd
[] = {0x12, 0x02, 0x30}; // Just buttons.
912 if (wiiremote
->isVibrationEnabled
) cmd
[1] |= 0x01;
915 There are numerous status report types that can be requested.
916 The IR reports must be matched with the data format set when initializing the IR camera:
917 0x36, 0x37 - 10 IR bytes go with Basic mode
918 0x33 - 12 IR bytes go with Extended mode
919 0x3e/0x3f - 36 IR bytes go with Full mode
921 The Nunchuk and Classic controller use 6 bytes to report their state, so the reports that
922 give more extension bytes don't provide any more info.
924 Buttons | Accelerometer | IR | Extension
925 --------------------+-------------------+-----------+-------------
926 0x30: Core Buttons | | |
927 0x31: Core Buttons | Accelerometer | |
928 0x32: Core Buttons | | | 8 bytes
929 0x33: Core Buttons | Accelerometer | 12 bytes |
930 0x34: Core Buttons | | | 19 bytes
931 0x35: Core Buttons | Accelerometer | | 16 bytes
932 0x36: Core Buttons | | 10 bytes | 9 bytes
933 0x37: Core Buttons | Accelerometer | 10 bytes | 6 bytes
934 ?? 0x38: Core Buttons and Accelerometer with 16 IR bytes ??
937 0x3e / 0x3f: Interleaved Core Buttons and Accelerometer with 16/36 IR bytes
941 if (wiiremote
->isIRSensorEnabled
)
943 if (wiiremote
->isExpansionPortEnabled
)
945 cmd
[2] = 0x36; // Buttons, 10 IR Bytes, 9 Extension Bytes
946 wiiremote
->wiiIRMode
= kWiiIRModeBasic
;
950 cmd
[2] = 0x33; // Buttons, Accelerometer, and 12 IR Bytes.
951 wiiremote
->wiiIRMode
= kWiiIRModeExtended
;
955 writeData(wiiremote
, (darr
){ wiiremote
->wiiIRMode
}, 0x04B00033, 1);
956 usleep(kWait
); // wait 10ms
960 if (wiiremote
->isExpansionPortEnabled
)
962 cmd
[2] = 0x34; // Buttons, 19 Extension Bytes
966 cmd
[2] = 0x30; // Buttons
970 if (wiiremote
->isMotionSensorEnabled
) cmd
[2] |= 0x01; // Add Accelerometer
972 usleep(kWait
); // wait 10ms
973 result
= sendCommand(wiiremote
, cmd
, 3);
978 //--------------------------------------------------------------------------------------------
980 Boolean
wiiremote_motionsensor(WiiRemoteRef wiiremote
, Boolean enabled
)
982 wiiremote
->isMotionSensorEnabled
= enabled
;
983 return requestUpdates(wiiremote
);
986 Boolean
wiiremote_vibration(WiiRemoteRef wiiremote
, Boolean enabled
)
989 wiiremote
->isVibrationEnabled
= enabled
;
990 return requestUpdates(wiiremote
);
993 Boolean
wiiremote_led(WiiRemoteRef wiiremote
, Boolean enabled1
, Boolean enabled2
, Boolean enabled3
, Boolean enabled4
)
995 unsigned char cmd
[] = {0x11, 0x00};
996 if (wiiremote
->isVibrationEnabled
) cmd
[1] |= 0x01;
997 if (enabled1
) cmd
[1] |= 0x10;
998 if (enabled2
) cmd
[1] |= 0x20;
999 if (enabled3
) cmd
[1] |= 0x40;
1000 if (enabled4
) cmd
[1] |= 0x80;
1002 wiiremote
->isLED1Illuminated
= enabled1
;
1003 wiiremote
->isLED2Illuminated
= enabled2
;
1004 wiiremote
->isLED3Illuminated
= enabled3
;
1005 wiiremote
->isLED4Illuminated
= enabled4
;
1007 return sendCommand(wiiremote
, cmd
, 2);
1010 Boolean
wiiremote_expansion(WiiRemoteRef wiiremote
, Boolean enabled
)
1012 wiiremote
->isExpansionPortEnabled
= enabled
;
1013 if (wiiremote
->isExpansionPortAttached
== false)
1015 wiiremote
->isExpansionPortEnabled
= false;
1019 readData(wiiremote
, 0x04A40020, 16); //get calbdata
1022 return requestUpdates(wiiremote
);
1025 Boolean
wiiremote_irsensor(WiiRemoteRef wiiremote
, Boolean enabled
)
1029 wiiremote
->isIRSensorEnabled
= enabled
;
1032 unsigned char cmd
[] = {0x13, 0x00};
1033 if (wiiremote
->isVibrationEnabled
) cmd
[1] |= 0x01;
1034 if (wiiremote
->isIRSensorEnabled
) cmd
[1] |= 0x04;
1035 if ((ret
= sendCommand(wiiremote
, cmd
, 2)) == false)
1039 // set register 0x1a (ir enable 2)
1040 unsigned char cmd2
[] = {0x1a, 0x00};
1041 if (enabled
) cmd2
[1] |= 0x04;
1042 if ((ret
= sendCommand(wiiremote
, cmd2
, 2)) == false)
1048 // based on marcan's method, found on wiili wiki:
1049 // tweaked to include some aspects of cliff's setup procedure in the hopes
1050 // of it actually turning on 100% of the time (was seeing 30-40% failure rate before)
1051 // the sleeps help it it seems
1053 if ((ret
= writeData(wiiremote
, (darr
){0x01}, 0x04B00030, 1)) == false) return ret
;
1055 if ((ret
= writeData(wiiremote
, (darr
){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0xC0}, 0x04B00000, 9)) == false) return ret
;
1057 if ((ret
= writeData(wiiremote
, (darr
){0x40, 0x00}, 0x04B0001A, 2)) == false) return ret
;
1059 if ((ret
= writeData(wiiremote
, (darr
){0x08}, 0x04B00030, 1)) == false) return ret
;
1062 requestUpdates(wiiremote
);
1066 // probably should do some writes to power down the camera, save battery
1067 // but don't know how yet.
1069 ret
= wiiremote_motionsensor(wiiremote
, wiiremote
->isMotionSensorEnabled
);
1070 ret
= wiiremote_vibration(wiiremote
, wiiremote
->isVibrationEnabled
);
1071 ret
= wiiremote_expansion(wiiremote
, wiiremote
->isExpansionPortEnabled
);
1077 Boolean
wiiremote_getstatus(WiiRemoteRef wiiremote
)
1079 unsigned char cmd
[] = {0x15, 0x00};
1080 return sendCommand(wiiremote
, cmd
, 2);