Procházet zdrojové kódy

Switch Pro: report non-charging state and per-slot battery level (#135)

* Update Switch Pro battery and charging flags

* Finalize Switch Pro battery and charging reporting

* fmt

* Both slots now work correctly

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Jari <safijari@isu.edu>
Copilot před 3 měsíci
rodič
revize
4d7182e37d

+ 3 - 2
OpenPuck/haptics.cpp

@@ -519,8 +519,9 @@ bool rfConnFlushRelay(uint8_t ch, uint8_t s1)
 			// full F1 decode (seq-dedup guards double-forward), so a drag streaming haptics now collects
 			// ~2x the samples, closing the gap to the real puck. A present reply returns early (~90us);
 			// only a genuine no-reply pays the bounded 400us window, so airtime stays in budget.
-			rfConnTx(ch, s1, p, plen,
-				 400); // one relay per poll cycle -- reply harvested as input
+			rfConnTx(
+				ch, s1, p, plen,
+				400); // one relay per poll cycle -- reply harvested as input
 		}
 	}
 	return have; // true = a relay frame went out this cycle (its reply is harvested as input, above)

+ 33 - 7
OpenPuck/mode_switch_pro.cpp

@@ -4,6 +4,7 @@
 #include "config.h"
 #include "haptics.h"
 #include "bonds.h"
+#include "rf_link.h"
 #include "usb_mount.h"
 #include "usb_tx.h"
 #include <Adafruit_TinyUSB.h>
@@ -222,6 +223,31 @@ static void jcPackStick(uint8_t s[3], int16_t x, int16_t y)
 	s[1] = (uint8_t)(((Y & 0x0F) << 4) | ((X >> 8) & 0x0F));
 	s[2] = (uint8_t)((Y >> 4) & 0xFF);
 }
+
+// The Switch battery/connection byte (bat_con) is NOT a 0..8 value in the high nibble. Per hid-nintendo it is:
+//   bits[7:5] = battery capacity (0=empty .. 4=full), bit4 = charging, bit0 = host_powered (USB-powered).
+// A genuine pad therefore only ever emits the EVEN high-nibble levels 8/6/4/2/0 (full/medium/low/critical/empty)
+// -- the odd bit (bit4) is reserved for the charging flag. The old code packed a 0..8 value straight into the
+// high nibble, so any ODD level (1,3,5,7) set bit4 and the console showed the pad as "charging". Here we round
+// the percentage onto the genuine 5-level scale and return the ready-to-place EVEN nibble {0,2,4,6,8}.
+static uint8_t jcBatteryNibble(uint8_t bond)
+{
+	if (bond >= NSLOT)
+		return 0;
+	uint8_t pct = g_battery[bond];
+	uint8_t cap; // genuine 0..4 capacity
+	if (pct >= 70)
+		cap = 4; // full
+	else if (pct >= 50)
+		cap = 3; // medium
+	else if (pct >= 30)
+		cap = 2; // low
+	else if (pct >= 10)
+		cap = 1; // critical
+	else
+		cap = 0; // empty
+	return (uint8_t)(cap << 1); // -> 0,2,4,6,8 (even; leaves bit4 clear)
+}
 // Standard input-report prefix [0..11] (timer, battery/conn, 3 button bytes, both packed sticks, vibrator),
 // shared by the streamed 0x30 report and the 0x21 subcommand-reply reports the host reads during init.
 static void jcInputPrefix(uint8_t slot, uint8_t *out)
@@ -284,16 +310,16 @@ static void jcInputPrefix(uint8_t slot, uint8_t *out)
 		jc |= codeToJc(g_qamMap, fA, fB, fX, fY);
 	out[0] = g_jcTimer[slot]++;
 
-	// battery full+charging (hi nibble 0x9), connection_info=1 (lo nibble): a wired/charging Pro Controller.
-	// A real Switch reads this to show the pad as connected; Steam/hid-nintendo accept it too.
-	out[1] = 0x91;
+	// bat_con byte: [7:5]=capacity, bit4=charging, bit0=host_powered (see jcBatteryNibble). The controllers are
+	// wireless (battery powered) from the console's view, so host_powered=0 -- setting it pins the pad the console
+	// treats as the wired/primary device in the charging state (the first-enumerated pad showing "charging, 100%").
+	// The charging flag reflects the controller's REAL EChargeState (2=charging); wireless pads report discharging
+	// (1) so it stays clear, but a pad genuinely on a charger will show the bolt correctly.
+	uint8_t chg = (bond < NSLOT && g_batteryState[bond] == 2) ? 0x10 : 0x00;
+	out[1] = (uint8_t)((jcBatteryNibble(bond) << 4) | chg);
 	out[2] = (uint8_t)(jc);
 	out[3] = (uint8_t)(jc >> 8);
 	out[4] = (uint8_t)(jc >> 16);
-
-	// charging_grip bit (button "common" byte, bit7): genuine Pro Controller always sets it on USB; real
-	// Switch uses it to recognise a wired controller. Not a button, so hid-nintendo ignores it.
-	out[3] |= 0x80;
 	jcPackStick(out + 5, g_in[bond].lx, g_in[bond].ly);
 	jcPackStick(out + 8, g_in[bond].rx, g_in[bond].ry);
 	// rumble_input_report echo: genuine pad emits 0x09..0x0C; some Switch firmware expects this nonzero.

+ 2 - 1
OpenPuck/puck_hid.cpp

@@ -741,7 +741,8 @@ void SteamPuckController::onReport45(int slot, const uint8_t *rep, bool fresh,
 		if (send && g_fwdContentDedup && blen >= 2) {
 			static uint8_t lastBody[NSLOT][53];
 			static uint8_t lastBodyLen[NSLOT] = { 0 };
-			uint8_t clen = (uint8_t)(blen - 1); // bytes after the counter
+			uint8_t clen =
+				(uint8_t)(blen - 1); // bytes after the counter
 			if (clen > sizeof lastBody[0])
 				clen = sizeof lastBody[0];
 			if (lastBodyLen[slot] == clen &&

+ 3 - 2
OpenPuck/serial_console.cpp

@@ -66,8 +66,9 @@ void serialConsolePoll()
 				else if (hz > 1000)
 					hz = 1000;
 				g_pollUs = (uint32_t)(1000000L / hz);
-				Serial.printf("# poll rate -> %ld Hz (%lu us)\n", hz,
-					      (unsigned long)g_pollUs);
+				Serial.printf(
+					"# poll rate -> %ld Hz (%lu us)\n", hz,
+					(unsigned long)g_pollUs);
 			} else if (!strcmp(line, "HR")) {
 				// A/B: disable the puck->controller haptic relay (Steam 0x80-0x86 rumble/pad feedback)
 				// to isolate whether relaying Steam's trackpad texture haptics degrades drag smoothness.