From c2cecd9b3cf8b84c52ad03c309e8ef9487083935 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gerard=20Gasc=C3=B3n?=
<52170489+GerardGascon@users.noreply.github.com>
Date: Thu, 6 Jun 2024 17:24:59 +0200
Subject: [PATCH] feat: run joycon.net sample
---
.../.idea/git_toolbox_prj.xml | 15 ++
Program.cs | 17 +-
SwitchSlidePresenter.csproj | 4 +
Tutorial.cs | 184 ++++++++++++++++++
4 files changed, 216 insertions(+), 4 deletions(-)
create mode 100644 .idea/.idea.SwitchSlidePresenter/.idea/git_toolbox_prj.xml
create mode 100644 Tutorial.cs
diff --git a/.idea/.idea.SwitchSlidePresenter/.idea/git_toolbox_prj.xml b/.idea/.idea.SwitchSlidePresenter/.idea/git_toolbox_prj.xml
new file mode 100644
index 0000000..02b915b
--- /dev/null
+++ b/.idea/.idea.SwitchSlidePresenter/.idea/git_toolbox_prj.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Program.cs b/Program.cs
index d8598d8..7795ee7 100644
--- a/Program.cs
+++ b/Program.cs
@@ -1,7 +1,16 @@
-namespace SwitchSlidePresenter;
+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;
-class Program {
- static void Main(string[] args) {
- Console.WriteLine("Hello, World!");
+namespace SwitchSlidePresenter {
+ class Program {
+ static async Task Main(string[] args) {
+ await Tutorial.Execute();
+ }
}
}
\ No newline at end of file
diff --git a/SwitchSlidePresenter.csproj b/SwitchSlidePresenter.csproj
index 2f4fc77..21b392a 100644
--- a/SwitchSlidePresenter.csproj
+++ b/SwitchSlidePresenter.csproj
@@ -7,4 +7,8 @@
enable
+
+
+
+
diff --git a/Tutorial.cs b/Tutorial.cs
new file mode 100644
index 0000000..d7dd9e2
--- /dev/null
+++ b/Tutorial.cs
@@ -0,0 +1,184 @@
+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 ?? ""}");
+ 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