feat: simplified tutorial to suit my needs
This commit is contained in:
parent
c2cecd9b3c
commit
4909f82bb2
4 changed files with 119 additions and 185 deletions
111
JoyConRead.cs
Normal file
111
JoyConRead.cs
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ using wtf.cluster.JoyCon.Rumble;
|
||||||
namespace SwitchSlidePresenter {
|
namespace SwitchSlidePresenter {
|
||||||
class Program {
|
class Program {
|
||||||
static async Task Main(string[] args) {
|
static async Task Main(string[] args) {
|
||||||
await Tutorial.Execute();
|
await JoyConRead.Read();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
7
SlideSwitchPress.cs
Normal file
7
SlideSwitchPress.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace SwitchSlidePresenter;
|
||||||
|
|
||||||
|
public enum SlideSwitchPress {
|
||||||
|
None,
|
||||||
|
Previous,
|
||||||
|
Next
|
||||||
|
}
|
184
Tutorial.cs
184
Tutorial.cs
|
@ -1,184 +0,0 @@
|
||||||
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.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue