Timon3
Team ModMii
- Registriert
- 17 Juli 2013
- Beiträge
- 499
Hallo zusammen!
Bei uns an der Schule ist momentan Facharbeitsphase. Dabei habe ich mich für Informatik entschieden (weil mir das Fach ziemlich viel Spaß macht). Für die Facharbeit wollte/will ich selbst die Daten der WiiMote "entschlüsseln" (also tatsächlich nutzbar machen) und darauf basierend eine Bibliothek schreiben. Klingt im ersten Moment ja nicht so komplex.
Also habe ich mir einen Bluetooth-Adapter geholt, alles installiert, WiiMote verbunden (klappt auch) und angefangen, indem ich mir den HID-Pfad hole. Klappt soweit, durch die VID und PID kann ich genau filtern, welches Device ich habe. Ich könnte das ganze durch P/Invoke natürlich selbst schreiben, aber es gibt dafür ganz ordentliche Bibliotheken (ich benutze diese hier. Das ist aber relativ egal, da ich auch den manuellen Weg schon erfolglos gegangen bin.
Zurück zum Problem. Jetzt will ich natürlich mit der Mote selbst was anstellen. Zuerstmal interessiert mich lesen. Dabei wird aber bei der asynchronen Operation das Callback nicht gerufen, und bei der synchronen kommt irgendwann eine Thread-Deadlock-Exception. Kacke, ich hab was falsch gemacht. Dachte ich. Aber dann hab ich mir einfach mal aus Jux aus einer bereits bestehenden Bibliothek abgeschaut, wie man genau die LEDs setzen kann. Kann doch jetzt gar nicht funktionieren! Aber ich hab es trotzdem mal probiert. Ergebnis: Ja, funktioniert ohne Probleme. Mist, was hab ich beim Lesen falsch gemacht? Nochmal probiert. Lesen geht. Jippie.
Nächster Morgen. PC angeschaltet, hochmotiviert weiter mit der Entwicklung gemacht - dachte ich, bis ich nochmal das lesen getestet habe. Funktioniert nicht. Hmpf. Anderen Bluetooth-Stack probiert - klappt genauso wenig. Anderen Bluetooth-Stick bestellt: Nada.
Da stehe ich nun also, mein Programm klappt nur sehr selten und vollkommen unreproduzierbar. In 95% aller Fälle kommt ein Read-Timeout oder ein Thread-Deadlock. Das nervigste ist: Ich hab meinen Code mit einer bereits bestehenden Bibliothek verglichen, die bei mir funktioniert. Ich hab keinen Unterschied gesehen.
Lange Rede, gar kein Sinn: Kann mir da jemand bei helfen? Ich wäre dafür jedenfalls unglaublich dankbar. Dieses Problem hat mich schon an die 10 Stunden googlen, neuinstallieren von verschiedensten Stacks, weinend in der Ecke liegen und noch mehr googlen gekostet.
Hier ist der betreffende Code:
Hier der betreffende Code der Bibliothek:
Bei uns an der Schule ist momentan Facharbeitsphase. Dabei habe ich mich für Informatik entschieden (weil mir das Fach ziemlich viel Spaß macht). Für die Facharbeit wollte/will ich selbst die Daten der WiiMote "entschlüsseln" (also tatsächlich nutzbar machen) und darauf basierend eine Bibliothek schreiben. Klingt im ersten Moment ja nicht so komplex.
Also habe ich mir einen Bluetooth-Adapter geholt, alles installiert, WiiMote verbunden (klappt auch) und angefangen, indem ich mir den HID-Pfad hole. Klappt soweit, durch die VID und PID kann ich genau filtern, welches Device ich habe. Ich könnte das ganze durch P/Invoke natürlich selbst schreiben, aber es gibt dafür ganz ordentliche Bibliotheken (ich benutze diese hier. Das ist aber relativ egal, da ich auch den manuellen Weg schon erfolglos gegangen bin.
Zurück zum Problem. Jetzt will ich natürlich mit der Mote selbst was anstellen. Zuerstmal interessiert mich lesen. Dabei wird aber bei der asynchronen Operation das Callback nicht gerufen, und bei der synchronen kommt irgendwann eine Thread-Deadlock-Exception. Kacke, ich hab was falsch gemacht. Dachte ich. Aber dann hab ich mir einfach mal aus Jux aus einer bereits bestehenden Bibliothek abgeschaut, wie man genau die LEDs setzen kann. Kann doch jetzt gar nicht funktionieren! Aber ich hab es trotzdem mal probiert. Ergebnis: Ja, funktioniert ohne Probleme. Mist, was hab ich beim Lesen falsch gemacht? Nochmal probiert. Lesen geht. Jippie.
Nächster Morgen. PC angeschaltet, hochmotiviert weiter mit der Entwicklung gemacht - dachte ich, bis ich nochmal das lesen getestet habe. Funktioniert nicht. Hmpf. Anderen Bluetooth-Stack probiert - klappt genauso wenig. Anderen Bluetooth-Stick bestellt: Nada.
Da stehe ich nun also, mein Programm klappt nur sehr selten und vollkommen unreproduzierbar. In 95% aller Fälle kommt ein Read-Timeout oder ein Thread-Deadlock. Das nervigste ist: Ich hab meinen Code mit einer bereits bestehenden Bibliothek verglichen, die bei mir funktioniert. Ich hab keinen Unterschied gesehen.
Lange Rede, gar kein Sinn: Kann mir da jemand bei helfen? Ich wäre dafür jedenfalls unglaublich dankbar. Dieses Problem hat mich schon an die 10 Stunden googlen, neuinstallieren von verschiedensten Stacks, weinend in der Ecke liegen und noch mehr googlen gekostet.
Hier ist der betreffende Code:
[src=csharp]public partial class Form1 : Form {
private const int VID = 0x057E;
private const int PID = 0x0306;
private const int REPORT_LENGTH = 22;
private static HidDevice wiiMote;
public Form1()
{
InitializeComponent();
}
private void debug(Object pObject) {
}
private void connect()
{
wiiMote = HidDevices.Enumerate(VID, PID).FirstOrDefault();
if (wiiMote != null)
{
wiiMote.OpenDevice();
wiiMote.MonitorDeviceEvents = true;
wiiMote.Write(new byte[1]);
}
else
{
this.Text = "No Wiimote found!";
}
}
private void button1_Click(object sender, EventArgs e)
{
if (wiiMote.IsConnected && wiiMote.IsOpen)
{
HidDeviceData data = wiiMote.Read();
if (data.Status == HidDeviceData.ReadStatus.Success)
{
for (int i = 0; i < 22; i++)
{
Control[] ctrls = this.Controls.Find("label" + (i + 1), true);
if (ctrls.Length == 1)
ctrls[0].Text = i.ToString("X8") + ": " + data.Data.ToString("X8") + " " + data.Data.ToString();
}
}
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
wiiMote.CloseDevice();
}
private void button2_Click(object sender, EventArgs e)
{
byte[] buff = new byte[2];
buff[0] = 0x11;
buff[1] = 0x20;
wiiMote.Write(buff);
}
private void Form1_Load(object sender, EventArgs e)
{
connect();
}
}[/src]
private const int VID = 0x057E;
private const int PID = 0x0306;
private const int REPORT_LENGTH = 22;
private static HidDevice wiiMote;
public Form1()
{
InitializeComponent();
}
private void debug(Object pObject) {
}
private void connect()
{
wiiMote = HidDevices.Enumerate(VID, PID).FirstOrDefault();
if (wiiMote != null)
{
wiiMote.OpenDevice();
wiiMote.MonitorDeviceEvents = true;
wiiMote.Write(new byte[1]);
}
else
{
this.Text = "No Wiimote found!";
}
}
private void button1_Click(object sender, EventArgs e)
{
if (wiiMote.IsConnected && wiiMote.IsOpen)
{
HidDeviceData data = wiiMote.Read();
if (data.Status == HidDeviceData.ReadStatus.Success)
{
for (int i = 0; i < 22; i++)
{
Control[] ctrls = this.Controls.Find("label" + (i + 1), true);
if (ctrls.Length == 1)
ctrls[0].Text = i.ToString("X8") + ": " + data.Data.ToString("X8") + " " + data.Data.ToString();
}
}
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
wiiMote.CloseDevice();
}
private void button2_Click(object sender, EventArgs e)
{
byte[] buff = new byte[2];
buff[0] = 0x11;
buff[1] = 0x20;
wiiMote.Write(buff);
}
private void Form1_Load(object sender, EventArgs e)
{
connect();
}
}[/src]
Hier der betreffende Code der Bibliothek:
[src=csharp]/// <summary>
/// Connect to a Wiimote paired to the PC via Bluetooth
/// </summary>
public void Connect()
{
int index = 0;
bool found = false;
Guid guid;
// get the GUID of the HID class
HIDImports.HidD_GetHidGuid(out guid);
// get a handle to all devices that are part of the HID class
// Fun fact: DIGCF_PRESENT worked on my machine just fine. I reinstalled Vista, and now it no longer finds the Wiimote with that parameter enabled...
IntPtr hDevInfo = HIDImports.SetupDiGetClassDevs(ref guid, null, IntPtr.Zero, HIDImports.DIGCF_DEVICEINTERFACE);// | HIDImports.DIGCF_PRESENT);
// create a new interface data struct and initialize its size
HIDImports.SP_DEVICE_INTERFACE_DATA diData = new HIDImports.SP_DEVICE_INTERFACE_DATA();
diData.cbSize = Marshal.SizeOf(diData);
// get a device interface to a single device (enumerate all devices)
while(HIDImports.SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref guid, index, ref diData))
{
UInt32 size;
// get the buffer size for this device detail instance (returned in the size parameter)
HIDImports.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref diData, IntPtr.Zero, 0, out size, IntPtr.Zero);
// create a detail struct and set its size
HIDImports.SP_DEVICE_INTERFACE_DETAIL_DATA diDetail = new HIDImports.SP_DEVICE_INTERFACE_DETAIL_DATA();
// yeah, yeah...well, see, on Win x86, cbSize must be 5 for some reason. On x64, apparently 8 is what it wants.
// someday I should figure this out. Thanks to Paul Miller on this...
diDetail.cbSize = (uint)(IntPtr.Size == 8 ? 8 : 5);
// actually get the detail struct
if(HIDImports.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref diData, ref diDetail, size, out size, IntPtr.Zero))
{
Debug.WriteLine(index + " " + diDetail.DevicePath + " " + Marshal.GetLastWin32Error());
// open a read/write handle to our device using the DevicePath returned
mHandle = HIDImports.CreateFile(diDetail.DevicePath, FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, HIDImports.EFileAttributes.Overlapped, IntPtr.Zero);
// create an attributes struct and initialize the size
HIDImports.HIDD_ATTRIBUTES attrib = new HIDImports.HIDD_ATTRIBUTES();
attrib.Size = Marshal.SizeOf(attrib);
// get the attributes of the current device
if(HIDImports.HidD_GetAttributes(mHandle.DangerousGetHandle(), ref attrib))
{
// if the vendor and product IDs match up
if(attrib.VendorID == VID && attrib.ProductID == PID)
{
Debug.WriteLine("Found it!");
found = true;
// create a nice .NET FileStream wrapping the handle above
mStream = new FileStream(mHandle, FileAccess.ReadWrite, REPORT_LENGTH, true);
// start an async read operation on it
BeginAsyncRead();
// read the calibration info from the controller
try
{
ReadCalibration();
}
catch
{
// if we fail above, try the alternate HID writes
mAltWriteMethod = true;
ReadCalibration();
}
// force a status check to get the state of any extensions plugged in at startup
GetStatus();
break;
}
else
{
// otherwise this isn't the controller, so close up the file handle
mHandle.Close();
}
}
}
else
{
// failed to get the detail struct
throw new WiimoteException("SetupDiGetDeviceInterfaceDetail failed on index " + index);
}
// move to the next device
index++;
}
// clean up our list
HIDImports.SetupDiDestroyDeviceInfoList(hDevInfo);
// if we didn't find a Wiimote, throw an exception
if(!found)
throw new WiimoteException("Wiimote not found in HID device list.");
}
/// <summary>
/// Disconnect from the controller and stop reading data from it
/// </summary>
public void Disconnect()
{
// close up the stream and handle
if(mStream != null)
mStream.Close();
if(mHandle != null)
mHandle.Close();
}
/// <summary>
/// Start reading asynchronously from the controller
/// </summary>
private void BeginAsyncRead()
{
// if the stream is valid and ready
if(mStream != null && mStream.CanRead)
{
// setup the read and the callback
byte[] buff = new byte[REPORT_LENGTH];
mStream.BeginRead(buff, 0, REPORT_LENGTH, new AsyncCallback(OnReadData), buff);
}
}
/// <summary>
/// Callback when data is ready to be processed
/// </summary>
/// <param name="ar">State information for the callback</param>
private void OnReadData(IAsyncResult ar)
{
// grab the byte buffer
byte[] buff = (byte[])ar.AsyncState;
try
{
// end the current read
mStream.EndRead(ar);
// parse it
if(ParseInputReport(buff))
{
// post an event
if(WiimoteChanged != null)
WiimoteChanged(this, new WiimoteChangedEventArgs(mWiimoteState));
}
// start reading again
BeginAsyncRead();
}
catch(OperationCanceledException)
{
Debug.WriteLine("OperationCanceledException");
}
}[/src]
/// Connect to a Wiimote paired to the PC via Bluetooth
/// </summary>
public void Connect()
{
int index = 0;
bool found = false;
Guid guid;
// get the GUID of the HID class
HIDImports.HidD_GetHidGuid(out guid);
// get a handle to all devices that are part of the HID class
// Fun fact: DIGCF_PRESENT worked on my machine just fine. I reinstalled Vista, and now it no longer finds the Wiimote with that parameter enabled...
IntPtr hDevInfo = HIDImports.SetupDiGetClassDevs(ref guid, null, IntPtr.Zero, HIDImports.DIGCF_DEVICEINTERFACE);// | HIDImports.DIGCF_PRESENT);
// create a new interface data struct and initialize its size
HIDImports.SP_DEVICE_INTERFACE_DATA diData = new HIDImports.SP_DEVICE_INTERFACE_DATA();
diData.cbSize = Marshal.SizeOf(diData);
// get a device interface to a single device (enumerate all devices)
while(HIDImports.SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref guid, index, ref diData))
{
UInt32 size;
// get the buffer size for this device detail instance (returned in the size parameter)
HIDImports.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref diData, IntPtr.Zero, 0, out size, IntPtr.Zero);
// create a detail struct and set its size
HIDImports.SP_DEVICE_INTERFACE_DETAIL_DATA diDetail = new HIDImports.SP_DEVICE_INTERFACE_DETAIL_DATA();
// yeah, yeah...well, see, on Win x86, cbSize must be 5 for some reason. On x64, apparently 8 is what it wants.
// someday I should figure this out. Thanks to Paul Miller on this...
diDetail.cbSize = (uint)(IntPtr.Size == 8 ? 8 : 5);
// actually get the detail struct
if(HIDImports.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref diData, ref diDetail, size, out size, IntPtr.Zero))
{
Debug.WriteLine(index + " " + diDetail.DevicePath + " " + Marshal.GetLastWin32Error());
// open a read/write handle to our device using the DevicePath returned
mHandle = HIDImports.CreateFile(diDetail.DevicePath, FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, HIDImports.EFileAttributes.Overlapped, IntPtr.Zero);
// create an attributes struct and initialize the size
HIDImports.HIDD_ATTRIBUTES attrib = new HIDImports.HIDD_ATTRIBUTES();
attrib.Size = Marshal.SizeOf(attrib);
// get the attributes of the current device
if(HIDImports.HidD_GetAttributes(mHandle.DangerousGetHandle(), ref attrib))
{
// if the vendor and product IDs match up
if(attrib.VendorID == VID && attrib.ProductID == PID)
{
Debug.WriteLine("Found it!");
found = true;
// create a nice .NET FileStream wrapping the handle above
mStream = new FileStream(mHandle, FileAccess.ReadWrite, REPORT_LENGTH, true);
// start an async read operation on it
BeginAsyncRead();
// read the calibration info from the controller
try
{
ReadCalibration();
}
catch
{
// if we fail above, try the alternate HID writes
mAltWriteMethod = true;
ReadCalibration();
}
// force a status check to get the state of any extensions plugged in at startup
GetStatus();
break;
}
else
{
// otherwise this isn't the controller, so close up the file handle
mHandle.Close();
}
}
}
else
{
// failed to get the detail struct
throw new WiimoteException("SetupDiGetDeviceInterfaceDetail failed on index " + index);
}
// move to the next device
index++;
}
// clean up our list
HIDImports.SetupDiDestroyDeviceInfoList(hDevInfo);
// if we didn't find a Wiimote, throw an exception
if(!found)
throw new WiimoteException("Wiimote not found in HID device list.");
}
/// <summary>
/// Disconnect from the controller and stop reading data from it
/// </summary>
public void Disconnect()
{
// close up the stream and handle
if(mStream != null)
mStream.Close();
if(mHandle != null)
mHandle.Close();
}
/// <summary>
/// Start reading asynchronously from the controller
/// </summary>
private void BeginAsyncRead()
{
// if the stream is valid and ready
if(mStream != null && mStream.CanRead)
{
// setup the read and the callback
byte[] buff = new byte[REPORT_LENGTH];
mStream.BeginRead(buff, 0, REPORT_LENGTH, new AsyncCallback(OnReadData), buff);
}
}
/// <summary>
/// Callback when data is ready to be processed
/// </summary>
/// <param name="ar">State information for the callback</param>
private void OnReadData(IAsyncResult ar)
{
// grab the byte buffer
byte[] buff = (byte[])ar.AsyncState;
try
{
// end the current read
mStream.EndRead(ar);
// parse it
if(ParseInputReport(buff))
{
// post an event
if(WiimoteChanged != null)
WiimoteChanged(this, new WiimoteChangedEventArgs(mWiimoteState));
}
// start reading again
BeginAsyncRead();
}
catch(OperationCanceledException)
{
Debug.WriteLine("OperationCanceledException");
}
}[/src]