Skip to content

Commit 0d5faa7

Browse files
committed
Added USB support.
Improved BT connection - no longer have to dis/reconnect controller fully from PC after usage.
1 parent 7081234 commit 0d5faa7

4 files changed

Lines changed: 108 additions & 783 deletions

File tree

BetterJoyForCemu/BetterJoyForCemu.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@
8888
<ItemGroup>
8989
<Compile Include="HIDapi.cs" />
9090
<Compile Include="Joycon.cs" />
91-
<Compile Include="MyQuaternion.cs" />
9291
<Compile Include="Program.cs" />
9392
<Compile Include="Properties\AssemblyInfo.cs" />
9493
<Compile Include="UpdServer.cs" />

BetterJoyForCemu/Joycon.cs

Lines changed: 88 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public class Joycon {
1313
float timing = 60.0f;
1414

1515
bool isPro = false;
16+
bool isUSB = false;
1617

1718
public enum DebugType : int {
1819
NONE,
@@ -219,7 +220,7 @@ public byte[] GetData() {
219220
public int packetCounter = 0;
220221
//
221222

222-
public Joycon(IntPtr handle_, bool imu, bool localize, float alpha, bool left, int id = 0, bool isPro=false) {
223+
public Joycon(IntPtr handle_, bool imu, bool localize, float alpha, bool left, int id = 0, bool isPro=false, bool usb = false) {
223224
handle = handle_;
224225
imu_enabled = imu;
225226
do_localize = localize;
@@ -229,6 +230,7 @@ public Joycon(IntPtr handle_, bool imu, bool localize, float alpha, bool left, i
229230

230231
PadId = id;
231232
this.isPro = isPro;
233+
isUSB = usb;
232234
}
233235
public void DebugPrint(String s, DebugType d) {
234236
if (debug_type == DebugType.NONE) return;
@@ -257,42 +259,72 @@ public Vector3 GetGyro() {
257259
public Vector3 GetAccel() {
258260
return acc_g;
259261
}
260-
public Quaternion GetVector() {
261-
Vector3 v1 = new Vector3(j_b.X, i_b.X, k_b.X);
262-
Vector3 v2 = -(new Vector3(j_b.Z, i_b.Z, k_b.Z));
263-
if (v2 != Vector3.Zero) {
264-
MyQuaternion temp = MyQuaternion.LookRotation(v1, v2);
265-
return new Quaternion(temp.eulerAngles, temp.Length);
266-
} else {
267-
return Quaternion.Identity;
268-
}
269-
}
270262
public int Attach(byte leds_ = 0x0) {
271263
state = state_.ATTACHED;
264+
265+
// Make sure command is received
266+
HIDapi.hid_set_nonblocking(handle, 0);
267+
272268
byte[] a = { 0x0 };
273-
// Input report mode
274-
Subcommand(0x3, new byte[] { 0x30 }, 1, false);
275-
Subcommand(0x3, new byte[] { 0x03, 0x00, 0x00, 0x01 }, 4, false); // higher gyro performance rate
276-
a[0] = 0x1;
277-
dump_calibration_data();
269+
278270
// Connect
279-
a[0] = 0x01;
280-
Subcommand(0x1, a, 1);
281-
a[0] = 0x02;
282-
Subcommand(0x1, a, 1);
283-
a[0] = 0x03;
284-
Subcommand(0x1, a, 1);
271+
if (!isUSB) {
272+
// Input report mode
273+
Subcommand(0x03, new byte[] { 0x30 }, 1, false);
274+
a[0] = 0x1;
275+
dump_calibration_data();
276+
} else {
277+
Subcommand(0x03, new byte[] { 0x3f }, 1, false);
278+
279+
a = Enumerable.Repeat((byte)0, 64).ToArray();
280+
Console.WriteLine("Using USB.");
281+
282+
a[0] = 0x80;
283+
a[1] = 0x01;
284+
HIDapi.hid_write(handle, a, new UIntPtr(2));
285+
HIDapi.hid_read(handle, a, new UIntPtr(64));
286+
287+
if (a[2] != 0x3) {
288+
PadMacAddress = new PhysicalAddress(new byte[] { a[9], a[8], a[7], a[6], a[5], a[4] });
289+
}
290+
291+
// USB Pairing
292+
a = Enumerable.Repeat((byte)0, 64).ToArray();
293+
a[0] = 0x80; a[1] = 0x02; // Handshake
294+
HIDapi.hid_write(handle, a, new UIntPtr(2));
295+
296+
a[0] = 0x80; a[1] = 0x03; // 3Mbit baud rate
297+
HIDapi.hid_write(handle, a, new UIntPtr(2));
298+
299+
a[0] = 0x80; a[1] = 0x02; // Handshake at new baud rate
300+
HIDapi.hid_write(handle, a, new UIntPtr(2));
301+
302+
a[0] = 0x80; a[1] = 0x04; // Prevent HID timeout
303+
HIDapi.hid_write(handle, a, new UIntPtr(2));
304+
305+
dump_calibration_data();
306+
}
307+
285308
a[0] = leds_;
286309
Subcommand(0x30, a, 1);
287310
Subcommand(0x40, new byte[] { (imu_enabled ? (byte)0x1 : (byte)0x0) }, 1, true);
288311
Subcommand(0x3, new byte[] { 0x30 }, 1, true);
289312
Subcommand(0x48, new byte[] { 0x1 }, 1, true);
313+
314+
if (!isUSB)
315+
Subcommand(0x41, new byte[] { 0x03, 0x00, 0x00, 0x01 }, 4, false); // higher gyro performance rate
316+
290317
DebugPrint("Done with init.", DebugType.COMMS);
318+
319+
HIDapi.hid_set_nonblocking(handle, 1);
320+
291321
return 0;
292322
}
323+
293324
public void SetFilterCoeff(float a) {
294325
filterweight = a;
295326
}
327+
296328
public void Detach() {
297329
stop_polling = true;
298330
PrintArray(max, format: "Max {0:S}", d: DebugType.IMU);
@@ -301,13 +333,21 @@ public void Detach() {
301333
//Subcommand(0x30, new byte[] { 0x0 }, 1); // Turn off LEDS after pair
302334
Subcommand(0x40, new byte[] { 0x0 }, 1);
303335
Subcommand(0x48, new byte[] { 0x0 }, 1);
336+
337+
if (isUSB) {
338+
byte[] a = Enumerable.Repeat((byte)0, 64).ToArray();
339+
a[0] = 0x80; a[1] = 0x05; // Allow device to talk to BT again
340+
HIDapi.hid_write(handle, a, new UIntPtr(2));
341+
}
342+
304343
//Subcommand(0x3, new byte[] { 0x3f }, 1); // Turn on basic HID mode - not needed
305344
}
306345
if (state > state_.DROPPED) {
307346
HIDapi.hid_close(handle);
308347
}
309348
state = state_.NOT_ATTACHED;
310349
}
350+
311351
private byte ts_en;
312352
private byte ts_de;
313353
private System.DateTime ts_prev;
@@ -328,6 +368,7 @@ private int ReceiveRaw() {
328368
}
329369
return ret;
330370
}
371+
331372
private Thread PollThreadObj;
332373
private void Poll() {
333374
int attempts = 0;
@@ -362,23 +403,19 @@ public void Update() {
362403
rep.CopyBuffer(report_buf);
363404
}
364405
if (imu_enabled) {
365-
if (do_localize) {
366-
ProcessIMU(report_buf);
367-
} else {
368-
ExtractIMUValues(report_buf, 0);
369-
// 3 values for 5ms precision instead of 15ms
370-
/*for (int n = 0; n < 3; n++) {
371-
ExtractIMUValues(report_buf, n);
372-
373-
Timestamp = rep.ts + (ulong) n * 5000; // 5ms difference
374-
375-
if (n == 0)
376-
ProcessButtonsAndStick(report_buf);
377-
378-
packetCounter++;
379-
Program.server.NewReportIncoming(this);
380-
}*/
381-
}
406+
ExtractIMUValues(report_buf, 0);
407+
// 3 values for 5ms precision instead of 15ms
408+
/*for (int n = 0; n < 3; n++) {
409+
ExtractIMUValues(report_buf, n);
410+
411+
Timestamp = rep.ts + (ulong) n * 5000; // 5ms difference
412+
413+
if (n == 0)
414+
ProcessButtonsAndStick(report_buf);
415+
416+
packetCounter++;
417+
Program.server.NewReportIncoming(this);
418+
}*/
382419
}
383420
if (ts_de == report_buf[1]) {
384421
DebugPrint(string.Format("Duplicate timestamp dequeued. TS: {0:X2}", ts_de), DebugType.THREADING);
@@ -483,90 +520,27 @@ private void ExtractIMUValues(byte[] report_buf, int n = 0) {
483520
else
484521
offset = right_hor_offset;
485522

486-
//Console.WriteLine("{0} {1} {2}", gyr_r[0], gyr_r[1], gyr_r[2]);
487-
488523
for (int i = 0; i < 3; ++i) {
489524
switch (i) {
490525
case 0:
491526
acc_g.X = (acc_r[i] - offset[i]) * (1.0f / (acc_sensiti[i] - acc_neutral[i])) * 4.0f;
492-
gyr_g.X = gyr_r[i] * (816.0f / (gyr_sensiti[i] - gyr_neutral[i]));
527+
gyr_g.X = (gyr_r[i] - gyr_neutral[i]) * (816.0f / (gyr_sensiti[i] - gyr_neutral[i]));
493528

494529
break;
495530
case 1:
496531
acc_g.Y = (acc_r[i] - offset[i]) * (1.0f / (acc_sensiti[i] - acc_neutral[i])) * 4.0f;
497-
gyr_g.Y = -gyr_r[i] * (816.0f / (gyr_sensiti[i] - gyr_neutral[i]));
532+
gyr_g.Y = -(gyr_r[i] - gyr_neutral[i] * 0.5f) * (816.0f / (gyr_sensiti[i] - gyr_neutral[i]));
498533

499534
break;
500535
case 2:
501536
acc_g.Z = (acc_r[i] - offset[i]) * (1.0f / (acc_sensiti[i] - acc_neutral[i])) * 4.0f;
502-
gyr_g.Z = -gyr_r[i] * (816.0f / (gyr_sensiti[i] - gyr_neutral[i]));
537+
gyr_g.Z = -(gyr_r[i] - gyr_neutral[i]) * (816.0f / (gyr_sensiti[i] - gyr_neutral[i]));
503538

504539
break;
505540
}
506541
}
507542
}
508543

509-
private float err;
510-
public Vector3 i_b, j_b, k_b, k_acc;
511-
private Vector3 d_theta;
512-
private Vector3 i_b_;
513-
private Vector3 w_a, w_g;
514-
private Quaternion vec;
515-
516-
private int ProcessIMU(byte[] report_buf) {
517-
518-
// Direction Cosine Matrix method
519-
// http://www.starlino.com/dcm_tutorial.html
520-
521-
if (!imu_enabled | state < state_.IMU_DATA_OK)
522-
return -1;
523-
524-
if (report_buf[0] != 0x30) return -1; // no gyro data
525-
526-
// read raw IMU values
527-
int dt = (report_buf[1] - timestamp);
528-
if (report_buf[1] < timestamp) dt += 0x100;
529-
530-
for (int n = 0; n < 3; ++n) {
531-
ExtractIMUValues(report_buf, n);
532-
533-
float dt_sec = 0.005f * dt;
534-
sum[0] += gyr_g.X * dt_sec;
535-
sum[1] += gyr_g.Y * dt_sec;
536-
sum[2] += gyr_g.Z * dt_sec;
537-
538-
if (isLeft && !isPro) { // not sure about this
539-
gyr_g.Y *= -1;
540-
gyr_g.Z *= -1;
541-
acc_g.Y *= -1;
542-
acc_g.Z *= -1;
543-
}
544-
545-
if (first_imu_packet) {
546-
i_b = new Vector3(1, 0, 0);
547-
j_b = new Vector3(0, 1, 0);
548-
k_b = new Vector3(0, 0, 1);
549-
first_imu_packet = false;
550-
} else {
551-
k_acc = -Vector3.Normalize(acc_g);
552-
w_a = Vector3.Cross(k_b, k_acc);
553-
w_g = -gyr_g * dt_sec;
554-
d_theta = (filterweight * w_a + w_g) / (1f + filterweight);
555-
k_b += Vector3.Cross(d_theta, k_b);
556-
i_b += Vector3.Cross(d_theta, i_b);
557-
j_b += Vector3.Cross(d_theta, j_b);
558-
//Correction, ensure new axes are orthogonal
559-
err = Vector3.Dot(i_b, j_b) * 0.5f;
560-
i_b_ = Vector3.Normalize(i_b - err * j_b);
561-
j_b = Vector3.Normalize(j_b - err * i_b);
562-
i_b = i_b_;
563-
k_b = Vector3.Cross(i_b, j_b);
564-
}
565-
dt = 1;
566-
}
567-
timestamp = report_buf[1] + 2;
568-
return 0;
569-
}
570544
public void Begin() {
571545
if (PollThreadObj == null) {
572546
PollThreadObj = new Thread(new ThreadStart(Poll));
@@ -575,10 +549,12 @@ public void Begin() {
575549
Console.WriteLine("Starting poll thread.");
576550
}
577551
}
552+
578553
public void Recenter() {
579554
first_imu_packet = true;
580555
}
581-
private float[] CenterSticks(UInt16[] vals, bool special=false) {
556+
557+
private float[] CenterSticks(UInt16[] vals, bool special = false) {
582558
ushort[] t = stick_cal;
583559

584560
if (special)
@@ -597,12 +573,14 @@ private float[] CenterSticks(UInt16[] vals, bool special=false) {
597573
}
598574
return s;
599575
}
576+
600577
public void SetRumble(float low_freq, float high_freq, float amp, int time = 0) {
601578
if (state <= Joycon.state_.ATTACHED) return;
602579
if (rumble_obj.timed_rumble == false || rumble_obj.t < 0) {
603580
rumble_obj = new Rumble(low_freq, high_freq, amp, time);
604581
}
605582
}
583+
606584
private void SendRumble(byte[] buf) {
607585
byte[] buf_ = new byte[report_len];
608586
buf_[0] = 0x10;
@@ -613,6 +591,7 @@ private void SendRumble(byte[] buf) {
613591
PrintArray(buf_, DebugType.RUMBLE, format: "Rumble data sent: {0:S}");
614592
HIDapi.hid_write(handle, buf_, new UIntPtr(report_len));
615593
}
594+
616595
private byte[] Subcommand(byte sc, byte[] buf, uint len, bool print = true) {
617596
byte[] buf_ = new byte[report_len];
618597
byte[] response = new byte[report_len];
@@ -630,6 +609,7 @@ private byte[] Subcommand(byte sc, byte[] buf, uint len, bool print = true) {
630609
else if (print) { PrintArray(response, DebugType.COMMS, report_len - 1, 1, "Response ID 0x" + string.Format("{0:X2}", response[0]) + ". Data: 0x{0:S}"); }
631610
return response;
632611
}
612+
633613
private void dump_calibration_data() {
634614
byte[] buf_ = ReadSPI(0x80, (isLeft ? (byte)0x12 : (byte)0x1d), 9); // get user calibration data if possible
635615
bool found = false;
@@ -730,6 +710,7 @@ private void dump_calibration_data() {
730710
PrintArray(gyr_neutral, len: 3, d: DebugType.IMU, format: "Factory gyro neutral position: {0:S}");
731711
}
732712
}
713+
733714
private byte[] ReadSPI(byte addr1, byte addr2, uint len, bool print = false) {
734715
byte[] buf = { addr2, addr1, 0x00, 0x00, (byte)len };
735716
byte[] read_buf = new byte[len];
@@ -745,6 +726,7 @@ private byte[] ReadSPI(byte addr1, byte addr2, uint len, bool print = false) {
745726
if (print) PrintArray(read_buf, DebugType.COMMS, len);
746727
return read_buf;
747728
}
729+
748730
private void PrintArray<T>(T[] arr, DebugType d = DebugType.NONE, uint len = 0, uint start = 0, string format = "{0:S}") {
749731
if (d != debug_type && debug_type != DebugType.ALL) return;
750732
if (len == 0) len = (uint)arr.Length;

0 commit comments

Comments
 (0)