Blitz3D now can control a WiiMote! (I hope)

Blitz3D Forums/Blitz3D Programming/Blitz3D now can control a WiiMote! (I hope)

That's my weekend work about WiiMote's

Hope some one found that interesting

First some .decls

file wii_kernel32.decls

.lib "Kernel32.dll" 

wiiCreateFile%(lpFileName$, dwDesiredAccess, dwShareMode, lpSecurrityAttributes, dwCreationDistribution, dwFlagsAndAttributes, hTemplateFile) : "CreateFileA"
wiiCreateFileOverlapped%(lpFileName$, dwDesiredAccess, dwShareMode, lpSecurrityAttributes*, dwCreationDistribution, dwFlagsAndAttributes, hTemplateFile) : "CreateFileA"
wiiCloseHandle%(hObject) : "CloseHandle" 
wiiWriteFile%(hFile, lpBuffer*, nNumberOfBytesToWrite, lpNumberOfBytesWritten*, lpOverlapped) : "WriteFile" 
wiiReadFile%(hFile, lpBuffer*, nNumberOfBytesToRead, lpNumberOfBytesRead*, lpOverlapped*) : "ReadFile"
wiiWaitForSingleObject%(nHandle, dwMilliseconds) : "WaitForSingleObject"
wiiCreateEvent%(lpSecurityAttributes*, bManualReset, bInitialState, lpName ) : "CreateEventA"
wiiResetEvent%(hEvent) : "ResetEvent"
wiiCancelIo%(hFile) : "CancelIo"


file wii_hid.decls

.lib "Hid.dll"

wiiHidD_GetAttributes%(HidDeviceObject, Attributes*): "HidD_GetAttributes"
wiiHidD_GetHidGuid%(HidGuid*) : "HidD_GetHidGuid"


file wii_setupapi.decls

.lib "setupapi.dll"

wiiSetupDiDestroyDeviceInfoList%(DeviceInfoSet) : "SetupDiDestroyDeviceInfoList"
wiiSetupDiEnumDeviceInterfaces%(DeviceInfoSet, DeviceInfoData, InterfaceClassGuid*, MemberIndex, DeviceInterfaceData*) : "SetupDiEnumDeviceInterfaces"
wiiSetupDiGetClassDevs%(ClassGuid*, Enumerator$, hwndParent, Flags) : "SetupDiGetClassDevsA"
wiiSetupDiGetDeviceInterfaceDetail%(DeviceInfoSet, DeviceInterfaceData*, DeviceInterfaceDetailData, DeviceInterfaceDetailDataSize, RequiredSize*, DeviceInfoData) : "SetupDiGetDeviceInterfaceDetailA"
wiiSetupDiGetDeviceInterfaceDetail2%(DeviceInfoSet, DeviceInterfaceData*, DeviceInterfaceDetailData*, DeviceInterfaceDetailDataSize, RequiredSize*, DeviceInfoData) : "SetupDiGetDeviceInterfaceDetailA"


and finally, one .bb: Wimote1.bb

;
;	I hope this should be of interest for some one else
;	I try to do the things easy, and I ask for help.... ...., wait, wait, ....
;		 but no one seems to be working with wiimotes in Blitz3d.
;
;							so that's my weekend work!
;
;
;	If some one need translation, ask me, I do what I can
;															(only if i's necesary)
;
;								Juan Ignacio Odriozola
;									jio@...


;*********************************************************************************
;
;	WiiMote desde Blitz3d
;
;		.decls necesarios: wii_hid.decls, wii_kernel32.decls, wii_setupapi.decls
;		(en las definiciones de las funciones se les ha incorporado wii como prefijo)
;
;	ObtenerHID: Busca un WiiMote, si lo encuentra fija HIDHandle para escritura y
;				ReadHandle para lectura solapada. Devuelve true o false según exito o no.
;
;	Escribir(Cantidad)	;transmite 22 bytes siempre (HIDHandle)
;						;previo hay que inicializar OutputReportData con la info a Tx
;						;Cantidad indica cuantos he inicializado, para que la rutina
;						;borre el resto del buffer
;
;	Leer(TOut)		;Ordena una lectura (ReadHandle), espera recibir 22 bytes o TOut
;					;Si operación de lectura exitosa devuelve True, si TOut devuelve False
;
;			Si Lectura exitosa: WiiMote\DReporting = Tipo de reporte recibido
;								WiiMote\RxCount	= cantidad de bytes recibidos
;								ReadBuffer contiene los bytes recibidos
;
;
;	Si BlueSolei ha detectado alguna vez al WiiMote, es encontrado en la lista por
;	ObtenerHID, se le asigna un HIDHandle y un ReadHandle.
;	Leer puede dar TimeOut porque el WiiMote se ha apagado, se quedó sin batería etc.
;
;
;
;	Uso: Teclas 1, 2, 3, 4 Prenden/Apagan los leds del WiiMote
;		 Tecla 5 apaga los 4 leds
;		 Tecla 6: Activa/Desactiva vibración (rumble)
;
;		 Esc: Fin del programa
;		 Depurando = true/false: debug on/off
;
;
;
;	"Básicamente traduje un programa Visual Basic 6" originalmente escrito por Jean Axelson
;	para la comunicación con HID, pensado en forma genérica para cualquier dispositivo USB
;	modificado por:  para encontrar un WiiMote.
;
;	Escribí las .decls desde el .bas de Visual en el que se declaraban las api's para visual.
;
;	modifique unas cuantas cosas, tanto que no se parece mucho al original, para que pudieran
;	ser usadas desde Blitz3d.
;
;	por ahora estoy conforme, espero le sirva a alguien más.
;
;
;	Referencias:
;
;	Trabajo original de Jean Axelson	
;					www.Lvr.com/hidpage.htm
;	Primera modificación para WiiMote
;					2007/1/9 tokkyo (http://onakasuita.org/wii/)
;					see <a href="http://www.wiili.org/index.php/Wiimote" target="_blank">http://www.wiili.org/index.php/Wiimote</a> For details
;
;
;	mucha información
;	<a href="http://wiibrew.org/index.php?title=Wiimote" target="_blank">http://wiibrew.org/index.php?title=Wiimote</a>	<<<<< Gracias mil!
;	
;
;*********************************************************************************



Const REGISTER_IR 					 = $04b00030
Const REGISTER_IR_SENSITIVITY_1 	 = $04b00000
Const REGISTER_IR_SENSITIVITY_2 	 = $04b0001a
Const REGISTER_IR_MODE 				 = $04b00033

Const REGISTER_EXTENSION_INIT 		 = $04a40040
Const REGISTER_EXTENSION_TYPE 		 = $04a400fe
Const REGISTER_EXTENSION_CALIBRATION = $04a40020


Global HIDHandle
Global ReadHandle
Global MyDeviceDetected=False

Global OutputReportData = CreateBank(255)
Global ReadBuffer 		= CreateBank( 22)
Global Security 		= CreateBank( 12)
Global HIDOverlapped 	= CreateBank( 20)

Global EventObject
Global Depurando=True

Type Controller

	Field Rumble = False
	Field TxContinuo = False
	Field Led1 = False
	Field Led2 = False
	Field Led3 = False
	Field Led4 = False
	
	Field IrMode = 0	;off, basic, extended, full: 00, 01, 03, 05
	Field DReporting	;Primer byte de la respuesta
	Field DRNoConocido
	Field RxCount
	Field Energia
	
	Field Botones1	; . . . + Up Dn Rg Lf
	Field Botones2	; H . . - A  B  1  2
	
	Field Boton_A 		= False
	Field Boton_B 		= False
	Field Boton_Minus 	= False
	Field Boton_Home 	= False
	Field Boton_Plus 	= False
	Field Boton_One 	= False
	Field Boton_Two 	= False
	Field Boton_Up 		= False
	Field Boton_Down 	= False
	Field Boton_Left 	= False
	Field Boton_Right 	= False

	Field AccelX
	Field Accely
	Field AccelZ
	
	Field PMx	;coordenadas del punto medio de P1 y P2 (IR)
	Field PMy

	Field P1x	;para los 4 puntos IR
	Field P1y
	Field P1t	;-1 si no se ve, o si no un campo Presente
	
	Field P2x
	Field P2y
	Field P2t

	Field P3x
	Field P3y
	Field P3t
	
	Field P4x
	Field P4y
	Field P4t
	
End Type

Global WiiMote.Controller = New Controller

SetBuffer BackBuffer()

MyDeviceDetected = ObtenerHID()

SolicitarEstado()

NoIR()

ModoDeRespuesta($31)

;continuous = off: solo tx si hay cambios
;

Repeat
	Cls

	LeoTeclado()
	
	If Leer(10) Then
		EvaluoRecepcion
	Else
		;time out
		Locate 10,100
		Write "TimeOut"
	End If

	;muestro variables del WiiMote
	;los últimos valores recibidos
	Locate 10,10
	Write WiiMote\AccelX
	Locate 40,10
	Write WiiMote\AccelY
	Locate 70,10
	Write WiiMote\AccelZ
	
	Locate 100,10
	Write "Batt: "+WiiMote\Energia
	
	Locate 180,10
	If WiiMote\Led1 Then 
		Write "1"
	Else
		Write "."
	End If
	
	Locate 190,10
	If WiiMote\Led2 Then 
		Write "1"
	Else
		Write "."
	End If
	
	Locate 200,10
	If WiiMote\Led3 Then 
		Write "1"
	Else
		Write "."
	End If
	
	Locate 210,10
	If WiiMote\Led4 Then 
		Write "1"
	Else
		Write "."
	End If
	
	
	Locate 10,30
	Write Right$(Bin$(WiiMote\Botones1),8) + "-" +Right$(Bin$(WiiMote\Botones2),8)
	
	Locate 200,30
	Write "?: "+WiiMote\DRNoConocido


	Flip True

Until KeyHit(1)

FreeBank HIDOverlapped

If MyDeviceDetected Then
	Result = wiiCloseHandle(HIDHandle)
	Result = wiiCloseHandle(ReadHandle)
End If

FreeBank Security
FreeBank OutputReportData
FreeBank ReadBuffer


End

;****************************************************************************************

Function LeoTeclado()

	If KeyHit(2) Then	;tecla 1
		WiiMote\Led1 = Not(WiiMote\Led1)
		ActualizarLeds()
	End If
	If KeyHit(3) Then	;tecla 2
		WiiMote\Led2 = Not(WiiMote\Led2)
		ActualizarLeds()
	End If
	If KeyHit(4) Then	;tecla 3
		WiiMote\Led3 = Not(WiiMote\Led3)
		ActualizarLeds()
	End If
	If KeyHit(5) Then	;tecla 4
		WiiMote\Led4 = Not(WiiMote\Led4)
		ActualizarLeds()
	End If
	If KeyHit(6) Then	;tecla 5 apaga todos los leds
		;leds apagados, rumble off
		ControlarLeds(False,False,False,False)
		Escribir(2)
		SolicitarEstado()
	End If
	If KeyHit(7) Then
		WiiMote\Rumble = Not(WiiMote\Rumble)
		ActualizarLeds()
	End If
End Function


Function ActualizarLeds()
	ControlarLeds(WiiMote\Led4,WiiMote\Led3,WiiMote\Led2,WiiMote\Led1)
End Function


Function ControlarLeds(l1,l2,l3,l4)
											;LEDS		0x11
	Dato=0
	If l1 Then Dato=$80
	If l2 Then Dato=Dato Or $40
	If l3 Then Dato=Dato Or $20
	If l4 Then Dato=Dato Or $10
	If WiiMote\Rumble Then Dato=Dato Or $01
	PokeByte(OutputReportData,0,$11)
	PokeByte(OutputReportData,1,Dato)
	Escribir(2)
End Function

Function NoIR()
	;disable IR
	PokeByte(OutputReportData,0,$13)
	PokeByte(OutputReportData,1,  0)
	Escribir(2)
	PokeByte(OutputReportData,0,$1A)
	PokeByte(OutputReportData,1,  0)
	Escribir(2)
	WiiMote\P1t = -1
	WiiMote\P2t = -1
	WiiMote\P3t = -1
	WiiMote\P4t = -1
End Function

Function ModoDeRespuesta(tipo)
											;TYPE		0x12
	PokeByte(OutputReportData,0,$12)
	PokeByte(OutputReportData,1,  0)
	PokeByte(OutputReportData,2,tipo)
	Escribir(3)
End Function

Function SolicitarEstado()
											;STATUS		0x15
	PokeByte(OutputReportData,0,$15)
	PokeByte(OutputReportData,1,  0)
	Escribir(2)	;status
End Function


; rutinas que analizan los datos del buffer de lectura
; son invocadas desde EvaluoRecepción, dependiento del tipo de reporte.

Function DeglozarBotones()
	Byte1 = PeekByte(ReadBuffer,1)
	Byte2 = PeekByte(ReadBuffer,2)
	WiiMote\Botones1 	= Byte1 And $1F	;0001 1111
	WiiMote\Botones2 	= Byte2 And $9F	;1001 1111
	WiiMote\Boton_A 	= (Byte2 And $08) <> 0
	WiiMote\Boton_B 	= (Byte2 And $04) <> 0
	WiiMote\Boton_Minus = (Byte2 And $10) <> 0
	WiiMote\Boton_Home 	= (Byte2 And $80) <> 0
	WiiMote\Boton_Plus 	= (Byte1 And $10) <> 0
	WiiMote\Boton_One 	= (Byte2 And $02) <> 0
	WiiMote\Boton_Two 	= (Byte2 And $01) <> 0
	WiiMote\Boton_Up 	= (Byte1 And $08) <> 0
	WiiMote\Boton_Down 	= (Byte1 And $04) <> 0
	WiiMote\Boton_Left 	= (Byte1 And $01) <> 0
	WiiMote\Boton_Right = (Byte1 And $02) <> 0
End Function

Function DeglozarAceleraciones()
	WiiMote\AccelX = PeekByte(ReadBuffer,3)
	WiiMote\AccelY = PeekByte(ReadBuffer,4)
	WiiMote\AccelZ = PeekByte(ReadBuffer,5)
End Function


Function EvaluoRecepcion()
	;se llama solo si se ha recibido ok	
	
	Select WiiMote\DReporting
		Case $20
			;status
			WiiMote\Energia = PeekByte(ReadBuffer,6)
			WiiMote\Led1 = (PeekByte(ReadBuffer,3) And $80) <> 0
			WiiMote\Led2 = (PeekByte(ReadBuffer,3) And $40) <> 0
			WiiMote\Led3 = (PeekByte(ReadBuffer,3) And $20) <> 0
			WiiMote\Led4 = (PeekByte(ReadBuffer,3) And $10) <> 0
			WiiMote\TxContinuo = (PeekByte(ReadBuffer,3) And $08) <> 0
		Case $21
			;Read
			
		Case $22
			;write
		
		Case $30
			DeglozarBotones()
		Case $31
			DeglozarBotones()
			DeglozarAceleraciones()
		Case $32
			DeglozarBotones()
		Case $33
			DeglozarBotones()
			DeglozarAceleraciones()
		Case $34
			DeglozarBotones()
		Case $35
			DeglozarBotones()
			DeglozarAceleraciones()
		Case $36
			DeglozarBotones()
		Case $37
			DeglozarBotones()
			DeglozarAceleraciones()
		Case $3D
		
		Case $3E
			DeglozarBotones()
		Case $3F
			DeglozarBotones()
		
		Default
			WiiMote\DRNoConocido = WiiMote\DRNoConocido + 1
	End Select
	
End Function

;funciones más que necesarias
;ObtenerHID, Leer, Escribir

Function Leer(TOut)

	;1) Si dispositivo no detectado, trata de encontrar uno
	;
	;2) Si dispositivo encotrado, manda leer
	;		si Lectura completada con éxito: devuelve true => ReadBuffer tiene lo recibido
	;		si TOut: cancela la lectura y devuelve false
	
	
	LecturaOk = False
	
	If MyDeviceDetected = False Then
		;intento encontrar uno
	    MyDeviceDetected = ObtenerHID()
	End If
	
	If MyDeviceDetected Then
		;si hay uno
		WAIT_TIMEOUT = $102
		WAIT_OBJECT_0 = $0
		
		NBRx = CreateBank(4)
		;Inicio la operación de lectura
		Result = wiiReadFile(ReadHandle, ReadBuffer, 22, NBRx, HIDOverlapped)
		;Regresa inmediatamente
		WiiMote\RxCount = PeekInt(NBRx,0)
		FreeBank NBRx
		;ahora espero a que lleguen los 22 o timeout
		Result = wiiWaitForSingleObject(EventObject, TOut)	;esto espera mismo.
		
		;ahora hay que evaluar los casos.
		
		Select Result
		    Case WAIT_OBJECT_0
	        	;ReadFile has completed
				WiiMote\DReporting = PeekByte(ReadBuffer,0)
				LecturaOk = True
	
		    Case WAIT_TIMEOUT
		        ;Timeout
		        ;Cancelo la operación
				Result = wiiCancelIo(ReadHandle)
				LecturaOk = False
	
			Default
				LecturaOk = False
	
		End Select
	
		wiiResetEvent(EventObject)
		
	End If
	
	Return LecturaOk		;true si habia un witmote y rx ok sin time out
							;false si habio uno y time out, o no habia y sigue sin haber

End Function



Function Escribir(Cantidad)

	;si dispositivo no detectado, trata de encontrar uno
	;si ya hay uno
	;	previo al llamado hay que ubicar en OutputReportData los datos a transmitir
	;	Cantidad indica cuantos hay en el buffer
	;	siempre se transmiten 22
	
	NumeroDeBytesEscritos = 0
	
	If MyDeviceDetected = False Then
	    MyDeviceDetected = ObtenerHID()
	Else
		OutBuffer=CreateBank(22)
		;copio de OutputReportData a OutBuffer solo Cantidad bytes
		;el resto se borra
		For i=0 To 21
			If i<Cantidad Then
				PokeByte(OutBuffer,i,PeekByte(OutputReportData,i))
			Else
				PokeByte(OutBuffer,i,0)
			End If
		Next
		Temp = CreateBank(4)
		Result = wiiWriteFile(HIDHandle, OutputReportData, 22, Temp, 0)
		NumeroDeBytesEscritos = PeekInt(Temp,0)
		FreeBank Temp
		FreeBank OutBuffer
	End If
	
	Return NumeroDeBytesEscritos

End Function



Function ObtenerHID()

	;busca en la lista de dispositivos el primer WiiMote
	;si lo encuentra, abre dos archivos HIDHandle para escritura 
	;y ReadHandle para lectura Overlapped
	;devuelve MyDeviceDetected que indica si la operación fué exitosa
	
	FILE_SHARE_READ = $1
	FILE_SHARE_WRITE = $2
	GENERIC_READ = $80000000
	GENERIC_WRITE = $40000000
	OPEN_EXISTING = 3
	FILE_FLAG_OVERLAPPED = $40000000
	
	GUID = CreateBank(16)	;long, int, int, array(0..7) byte
	;son estructuras, en las que el primer elemento (4 bytes) indica su tamaño
	;se pasan por referencia y hay que inicializar su tamaño antes de usarlas
	
	MyDeviceInterfaceDetailData = CreateBank(5)
	PokeInt(MyDeviceInterfaceDetailData,0,5)
	
	MyDeviceInfoData = CreateBank(28)
	PokeInt(MyDeviceInfoData,0,28)

	MyDeviceInterfaceData = CreateBank(28)
	PokeInt(MyDeviceInterfaceData,0,28)
	
	;esta estructura es escrita por HidD_GetAttributes, de ella se saca Vendedor, producto y versión
	DeviceAttributes = CreateBank(10)	;size(4), VendorID, ProductID, Version 2,2,2.
	
	DeviceInfoSet=0
	DevicePathName$=""
	Flags = 16 + 2
	DetailData=0
	LastDevice=False
	Detectado = False
	
	;Security es una estructura con tres variables tipo long
	PokeInt(Security, 0,  12)	;length 
	PokeInt(Security, 4,   0)	;lpSecurityDescriptor
	PokeInt(Security, 8,True)	;bInheritHandle
	
	
	HIDHandle = -1

	Result = wiiHidD_GetHidGuid(GUID)
	
	If Depurando Then
		DebugLog t
		DebugLog Hex(PeekInt(GUID,0))
		DebugLog Right$(Hex(PeekShort(GUID,4)),4)
		DebugLog Right$(Hex(PeekShort(GUID,6)),4)
		For i=8 To 15
			DebugLog Right$(Hex(PeekByte(GUID,i)),2)
		Next
	End If
	
	DeviceInfoSet = wiiSetupDiGetClassDevs(GUID,"",0,Flags)
	
	MemberIndex=0
	
	Needed = CreateBank(4)
	
	
	Repeat
	
		;PokeInt(MyDeviceInterfaceData,0,28)
		Result = wiiSetupDiEnumDeviceInterfaces(DeviceInfoSet,0,GUID,MemberIndex,MyDeviceInterfaceData)
		    
		If Result = 0 Then 
			LastDevice = True
		Else
	
			;PokeInt(MyDeviceInfoData,0,28)
			
			;tercer parámetro por valor
	        Result = wiiSetupDiGetDeviceInterfaceDetail(DeviceInfoSet, MyDeviceInterfaceData, 0, 0, Needed, 0)
	        
	        DetailData = PeekInt(Needed,0)
	
			;PokeInt(MyDeviceInterfaceDetailData,0,5)
	        
	        DetailDataBuffer = CreateBank(DetailData)
			Cuantos = DetailData
			PokeInt(DetailDataBuffer,0,5)
			
			;tercer parámetro por referencia
			Result = wiiSetupDiGetDeviceInterfaceDetail2(DeviceInfoSet, MyDeviceInterfaceData, DetailDataBuffer, DetailData, Needed, 0)
			
			strtmp$ = ""
			For i=4 To Cuantos-1
				strtmp$ = strtmp$ + Chr$(PeekByte(DetailDataBuffer,i))
			Next
			DebugLog strtmp$
			
			FreeBank DetailDataBuffer
			
			DevicePathName$ = strtmp$
			
			HIDHandle = wiiCreateFileOverlapped(DevicePathName, GENERIC_READ Or GENERIC_WRITE, (FILE_SHARE_READ Or FILE_SHARE_WRITE), Security, OPEN_EXISTING, 0, 0)
	
			If Depurando Then DebugLog "Handle ID: "+HIDHandle
	        
			Result = wiiHidD_GetAttributes(HIDHandle, DeviceAttributes)
			
			VendorID = PeekShort(DeviceAttributes,4)
			ProductID = PeekShort(DeviceAttributes,6)
			
			If Depurando Then DebugLog "VendorID : "+Hex(VendorID)+"  Product: "+Hex(ProductID)
	
			If (VendorID = $057E) And (ProductID = $0306) Then
	           	Detectado = True
				ReadHandle = wiiCreateFileOverlapped(DevicePathName, GENERIC_READ Or GENERIC_WRITE, (FILE_SHARE_READ Or FILE_SHARE_WRITE), Security, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0)
				;preparativos para la transferencia solapada
				If EventObject = 0 Then
					EventObject = wiiCreateEvent(Security,True,True,"")
				End If
				;HIDOverlapped es una estructura con 5 variables tipo Long
				;Internal, InternalHigh, Offset, OffsetHigh, hEvent
				PokeInt(HIDOverlapped, 8,0)				;offset
				PokeInt(HIDOverlapped,12,0)				;offsethigh
				PokeInt(HIDOverlapped,16,EventObject)	;hEvent
				
				If Depurando Then DebugLog "WiiMote encontrado"
	        Else
				Detectado = False
				If Depurando Then DebugLog "No es el que yo busco"
				Result = wiiCloseHandle(HIDHandle)
	        End If
	
		End If
		
		MemberIndex = MemberIndex+1
	
	Until (LastDevice = True) Or (Detectado = True)
	
	FreeBank Needed
	
	Result = wiiSetupDiDestroyDeviceInfoList(DeviceInfoSet)

	FreeBank GUID
	FreeBank MyDeviceInterfaceData
	FreeBank MyDeviceInterfaceDetailData
	FreeBank MyDeviceInfoData
	FreeBank DeviceAttributes

	Return Detectado

End Function


Cool, Ive been also wanting this for B3D so thanks for your effort. Unfortunately I can't test it until I back home in a few days. Have you managed to map all the features, including IR Dot Size?

it's still in beta stage

only detect, read and write and in the example some way's to do some specific tasks: read buttons, set led's, read accel's etc.

Init IR, I saw is not easy, the rest seem's to be simple.

Feedback will be appreciated!

Juan

cool