Commit 4909f82b authored by Gerard Gascón's avatar Gerard Gascón
Browse files

feat: simplified tutorial to suit my needs

parent c2cecd9b
Loading
Loading
Loading
Loading

JoyConRead.cs

0 → 100644
+111 −0
Original line number Diff line number Diff line
using System.Text;
using HidSharp;
using wtf.cluster.JoyCon;
using wtf.cluster.JoyCon.Calibration;
using wtf.cluster.JoyCon.ExtraData;
using wtf.cluster.JoyCon.HomeLed;
using wtf.cluster.JoyCon.InputReports;
using wtf.cluster.JoyCon.Rumble;

namespace SwitchSlidePresenter;

public class JoyConRead {
	public static async Task Read() {
		Console.OutputEncoding = Encoding.UTF8;

		HidDevice? device = null;
		// Get a list of all HID devices
		DeviceList list = DeviceList.Local;
		if (OperatingSystem.IsWindows()) {
			// Get all devices developed by Nintendo by vendor ID
			var nintendos = list.GetHidDevices(0x057e);
			// Get the first Nintendo controller
			device = nintendos.FirstOrDefault();
			// It works fine for Windows, but...
		} else {
			// Linux has more complicated HID device management, we can't get device's vendor ID,
			// so let's filter devices by their report descriptor
			// It should work in Windows too, so it's more multiplatform solution
			var hidDevices = list.GetHidDevices();
			foreach (var d in hidDevices) {
				var rd = d.GetReportDescriptor();
				if (rd != null) {
					if (
						rd.OutputReports.Count() == 4
						&& rd.OutputReports.Count(r => r.ReportID == 0x01) == 1
						&& rd.OutputReports.Count(r => r.ReportID == 0x10) == 1
						&& rd.OutputReports.Count(r => r.ReportID == 0x11) == 1
						&& rd.OutputReports.Count(r => r.ReportID == 0x12) == 1
						&& rd.InputReports.Count() == 6
						&& rd.InputReports.Count(r => r.ReportID == 0x21) == 1
						&& rd.InputReports.Count(r => r.ReportID == 0x30) == 1
						&& rd.InputReports.Count(r => r.ReportID == 0x31) == 1
						&& rd.InputReports.Count(r => r.ReportID == 0x32) == 1
						&& rd.InputReports.Count(r => r.ReportID == 0x33) == 1
						&& rd.InputReports.Count(r => r.ReportID == 0x3F) == 1
					) {
						device = d;
						break;
					}
				}
			}
		}
		if (device == null) {
			Console.WriteLine("No controller. Please connect Joy-Con or Pro controller via Bluetooth.");
			return;
		}
		// Create a new JoyCon instance based on the HID device
		JoyCon joycon = new(device);
		// Start controller polling
		joycon.Start();

		// First of all, we need to set format of the input reports,
		// most of the time you will need to use InputReportMode.Full mode
		await joycon.SetInputReportModeAsync(JoyCon.InputReportType.Simple);

		// Get some information about the controller
		DeviceInfo deviceInfo = await joycon.GetDeviceInfoAsync();
		Console.WriteLine(
			$"Type: {deviceInfo.ControllerType}, Firmware: {deviceInfo.FirmwareVersionMajor}.{deviceInfo.FirmwareVersionMinor}");
		var serial = await joycon.GetSerialNumberAsync();
		Console.WriteLine($"Serial number: {serial ?? "<none>"}");
		ControllerColors? colors = await joycon.GetColorsAsync();
		if (colors != null) {
			Console.WriteLine($"Body color: {colors.BodyColor}, buttons color: {colors.ButtonsColor}");
		} else {
			Console.WriteLine("Colors not specified, seems like the controller is grey.");
		}

		// Save the current cursor position
		(var cX, var cY) = (Console.CursorLeft, Console.CursorTop);

		// Subscribe to the input reports
		joycon.ReportReceived += (sender, input) => {
			Console.SetCursorPosition(cX, cY);
			// Check the type of the input report, most likely it will be InputWithImu
			if (input is InputSimple j) {
				// Some base data from the controller
				Console.WriteLine(
					$"Buttons: ({j.Buttons})          ");
			} else {
				Console.WriteLine($"Invalid input report type: {input.GetType()}");
			}
			return Task.CompletedTask;
		};

		// Error handling
		joycon.StoppedOnError += (_, ex) => {
			Console.WriteLine();
			Console.WriteLine($"Critical error: {ex.Message}");
			Console.WriteLine("Controller polling stopped.");
			Environment.Exit(1);
			return Task.CompletedTask;
		};

		Console.ReadKey();
		joycon.Stop();

		Console.WriteLine();
		Console.WriteLine("Stopped.");
	}
}
 No newline at end of file
+1 −1
Original line number Diff line number Diff line
@@ -10,7 +10,7 @@ using wtf.cluster.JoyCon.Rumble;
namespace SwitchSlidePresenter {
	class Program {
		static async Task Main(string[] args) {
			await Tutorial.Execute();
			await JoyConRead.Read();
		}
	}
}
 No newline at end of file

SlideSwitchPress.cs

0 → 100644
+7 −0
Original line number Diff line number Diff line
namespace SwitchSlidePresenter;

public enum SlideSwitchPress {
	None,
	Previous,
	Next
}
 No newline at end of file

Tutorial.cs

deleted100644 → 0
+0 −184
Original line number Diff line number Diff line
using HidSharp;
using System.Text;
using wtf.cluster.JoyCon;
using wtf.cluster.JoyCon.Calibration;
using wtf.cluster.JoyCon.ExtraData;
using wtf.cluster.JoyCon.HomeLed;
using wtf.cluster.JoyCon.InputReports;
using wtf.cluster.JoyCon.Rumble;

namespace SwitchSlidePresenter {
	public class Tutorial {
		public static async Task Execute() {
			Console.OutputEncoding = Encoding.UTF8;

			HidDevice? device = null;
			// Get a list of all HID devices
			DeviceList list = DeviceList.Local;
			if (OperatingSystem.IsWindows()) {
				// Get all devices developed by Nintendo by vendor ID
				var nintendos = list.GetHidDevices(0x057e);
				// Get the first Nintendo controller
				device = nintendos.FirstOrDefault();
				// It works fine for Windows, but...
			} else {
				// Linux has more complicated HID device management, we can't get device's vendor ID,
				// so let's filter devices by their report descriptor
				// It should work in Windows too, so it's more multiplatform solution
				var hidDevices = list.GetHidDevices();
				foreach (var d in hidDevices) {
					var rd = d.GetReportDescriptor();
					if (rd != null) {
						if (
							rd.OutputReports.Count() == 4
							&& rd.OutputReports.Count(r => r.ReportID == 0x01) == 1
							&& rd.OutputReports.Count(r => r.ReportID == 0x10) == 1
							&& rd.OutputReports.Count(r => r.ReportID == 0x11) == 1
							&& rd.OutputReports.Count(r => r.ReportID == 0x12) == 1
							&& rd.InputReports.Count() == 6
							&& rd.InputReports.Count(r => r.ReportID == 0x21) == 1
							&& rd.InputReports.Count(r => r.ReportID == 0x30) == 1
							&& rd.InputReports.Count(r => r.ReportID == 0x31) == 1
							&& rd.InputReports.Count(r => r.ReportID == 0x32) == 1
							&& rd.InputReports.Count(r => r.ReportID == 0x33) == 1
							&& rd.InputReports.Count(r => r.ReportID == 0x3F) == 1
						) {
							device = d;
							break;
						}
					}
				}
			}
			if (device == null) {
				Console.WriteLine("No controller. Please connect Joy-Con or Pro controller via Bluetooth.");
				return;
			}
			// Create a new JoyCon instance based on the HID device
			var joycon = new JoyCon(device);
			// Start controller polling
			joycon.Start();

			// First of all, we need to set format of the input reports,
			// most of the time you will need to use InputReportMode.Full mode
			await joycon.SetInputReportModeAsync(JoyCon.InputReportType.Full);

			// Get some information about the controller
			DeviceInfo deviceInfo = await joycon.GetDeviceInfoAsync();
			Console.WriteLine(
				$"Type: {deviceInfo.ControllerType}, Firmware: {deviceInfo.FirmwareVersionMajor}.{deviceInfo.FirmwareVersionMinor}");
			var serial = await joycon.GetSerialNumberAsync();
			Console.WriteLine($"Serial number: {serial ?? "<none>"}");
			ControllerColors? colors = await joycon.GetColorsAsync();
			if (colors != null) {
				Console.WriteLine($"Body color: {colors.BodyColor}, buttons color: {colors.ButtonsColor}");
			} else {
				Console.WriteLine("Colors not specified, seems like the controller is grey.");
			}

			// Enable IMU (accelerometer and gyroscope)
			await joycon.EnableImuAsync(true);
			// Enable rumble feature (it's enabled by default, actually)
			await joycon.EnableRumbleAsync(true);
			// You can control LEDs on the controller
			await joycon.SetPlayerLedsAsync(JoyCon.LedState.Off, JoyCon.LedState.On, JoyCon.LedState.Off,
				JoyCon.LedState.Blinking);

			// Get factory calibration data
			CalibrationData facCal = await joycon.GetFactoryCalibrationAsync();
			// Get user calibration data
			CalibrationData userCal = await joycon.GetUserCalibrationAsync();
			// Combine both calibrations, e.g. user calibration has higher priority
			CalibrationData calibration = facCal + userCal;
			// Get some parameters for the sticks
			StickParametersSet sticksParameters = await joycon.GetStickParametersAsync();

			// Home LED dimming pattern demo. Useless, but fun.
			await joycon.SetHomeLedDimmingPatternAsync(new HomeLedDimmingPattern {
				StepDurationBase = 4, // base duration is 40ms
				StartLedBrightness = 0, // 0%, off
				FullCyclesNumber = 0, // infinite
				HomeLedDimmingSteps = {
					new HomeLedDimmingStep {
						LedBrightness = 0x0F, // 100%
						TransitionDuration = 2, // base * 2 = 40ms * 2 = 80ms
						PauseDuration = 4, // base * 4 = 40ms * 4 = 160ms
					},
					new HomeLedDimmingStep {
						LedBrightness = 0x00, // LED off
						TransitionDuration = 2, // base * 2 = 40ms * 2 = 80ms
						PauseDuration = 4, // base * 4 = 40ms * 4 = 160ms
					},
					new HomeLedDimmingStep { LedBrightness = 0x0F, TransitionDuration = 2, PauseDuration = 4 },
					new HomeLedDimmingStep { LedBrightness = 0x00, TransitionDuration = 2, PauseDuration = 4 },
					new HomeLedDimmingStep { LedBrightness = 0x0F, TransitionDuration = 2, PauseDuration = 4 },
					new HomeLedDimmingStep { LedBrightness = 0x00, TransitionDuration = 2, PauseDuration = 4 },
					new HomeLedDimmingStep {
						LedBrightness = 0x08, // 50%
						TransitionDuration = 8, // base * 8 = 40ms * 8 = 320ms
						PauseDuration = 4, // base * 8 = 40ms * 8 = 320ms
					},
					new HomeLedDimmingStep {
						LedBrightness = 0x00, // LED off
						TransitionDuration = 8, // base * 8 = 40ms * 8 = 320ms
						PauseDuration = 15, // base * 15 = 40ms * 15 = 600ms
					}
				},
			});

			// Save the current cursor position
			(var cX, var cY) = (Console.CursorLeft, Console.CursorTop);

			// Subscribe to the input reports
			joycon.ReportReceived += (sender, input) => {
				Console.SetCursorPosition(cX, cY);
				// Check the type of the input report, most likely it will be InputWithImu
				if (input is InputFullWithImu j) {
					// Some base data from the controller
					Console.WriteLine(
						$"LeftStick: ({j.LeftStick}), RightStick: ({j.RightStick}), Buttons: ({j.Buttons}), Battery: {j.Battery}, Charging: {j.Charging}          ");
					// But we need calibrated data
					StickPositionCalibrated leftStickCalibrated =
						j.LeftStick.GetCalibrated(calibration.LeftStickCalibration!,
							sticksParameters.LeftStickParameters.DeadZone);
					StickPositionCalibrated rightStickCalibrated =
						j.RightStick.GetCalibrated(calibration.RightStickCalibration!,
							sticksParameters.RightStickParameters.DeadZone);
					Console.WriteLine(
						$"Calibrated LeftStick: (({leftStickCalibrated}), RightStick: ({rightStickCalibrated}))          ");
					// And accelerometer and gyroscope data
					ImuFrameCalibrated calibratedImu = j.Imu.Frames[0].GetCalibrated(calibration.ImuCalibration!);
					Console.WriteLine($"Calibrated IMU: ({calibratedImu})          ");
				} else {
					Console.WriteLine($"Invalid input report type: {input.GetType()}");
				}
				return Task.CompletedTask;
			};

			// Error handling
			joycon.StoppedOnError += new JoyCon.StoppedOnErrorHandler((_, ex) => {
				Console.WriteLine();
				Console.WriteLine($"Critical error: {ex.Message}");
				Console.WriteLine("Controller polling stopped.");
				Environment.Exit(1);
				return Task.CompletedTask;
			});

			// Periodically send rumble command to the controller
			var rumbleTimer = new Timer(async _ => {
				try {
					await joycon.WriteRumble(new RumbleSet(new RumbleData(40.9, 1), new RumbleData(65, 1)));
				}
				catch (Exception e) {
					Console.WriteLine($"Rumble error: {e.Message}");
				}
			}, null, 0, 1000);

			Console.ReadKey();
			await rumbleTimer.DisposeAsync();
			joycon.Stop();

			Console.WriteLine();
			Console.WriteLine("Stopped.");
		}
	}
}
 No newline at end of file