-
Notifications
You must be signed in to change notification settings - Fork 217
Saitex X52 Y rotation not working #283
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I would expect the product ID to be different on the X52 than on the Pro, so the device will probably not open on the server. static const vrpn_uint16 SAITEK_VENDOR = 0x06a3; If you can use vrpn_hid_device_watcher on that device, then it exists and you can see if you get data from it. What operating system are you running on? |
Russel:
Thanks for your reply.
In my test everything works ok except the top knog on the throttle which
the X52 HOTAS properties tool refers to as Y Rotation. Well that and
Trigger1 but that does not show in the X52 either. I assume there is a bad
switch in the stick
I am running Microsoft 10 and using Visual Studio 2017 for development.
Bruce
…On Wed, Jan 18, 2023 at 8:22 AM Russell Taylor ***@***.***> wrote:
I would expect the product ID to be different on the X52 than on the Pro,
so the device will probably not open on the server.
static const vrpn_uint16 SAITEK_VENDOR = 0x06a3;
static const vrpn_uint16 ST290_PRO = 0x0d60;
If you can use vrpn_hid_device_watcher on that device, then it exists and
you can see if you get data from it.
What operating system are you running on?
—
Reply to this email directly, view it on GitHub
<#283 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/ADNLDTS4QZFWZUCKMMGZIP3WS7VCNANCNFSM6AAAAAAT46TWSQ>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
That's very promising. The decodePacket() function at https://github.com/vrpn/vrpn/blob/master/vrpn_Saitek_Controller_Raw.C#L180 would need to be generalized to handle the additional analog and button when they exist. You can use the HID device watcher mentioned above to print hex values for all of the bytes and watch how they change when you change the controller to figure out where things are encoded. I'd love to get a pull request with the updated parser when you're done. Or you can send me one of the devices and I'll do it. :-) |
Russell:
When I run the script below from the power shell it shows "Saitek X52
H.O.T.A.S. (HID) HID\VID_06A3&PID_075C\6&3B730EA2&0&0000
Get-PnPDevice -Class HIDClass | where {
$_.HardwareID.Contains("HID_DEVICE_SYSTEM_GAME") } | sort Manufacturer |
select Manufacturer,Name,DeviceID
When I run vrpn_hid_device_watcher.exe 0x06a3 0x075c I get
Will accept HID device number 0 that has vendor:product 0000:0000
HID initialized.
Could not connect.
BruceMessage ID: ***@***.***>
… |
You need to convert the hexadecimal vendor and product IDs to decimal and put in the integers. It was taking your inputs as 0 0. |
Russell:
I ran the test as suggested results to follow. The seventh column shown
below is the correct one for the top dial. the values change as I rotate
the dial
G:\SupportLibraries3\vrpn\x64\Debug>vrpn_hid_device_watcher.exe 1699 1884 0
Will accept HID device number 0 that has vendor:product 06a3:075c
HID initialized.
Connected: HID device vendor ID 1699, product ID 1884, aka 06a3:075c
Entering update loop.
14 bytes: 00 04 20 80 7F FF 19 FF 00 00 00 00 00 88
14 bytes: 00 04 20 80 7F FF 18 FF 00 00 00 00 00 88
14 bytes: 00 04 20 80 7F FF 17 FF 00 00 00 00 00 88
14 bytes: 00 04 20 80 7F FF 15 FF 00 00 00 00 00 88
14 bytes: 00 04 20 80 7F FF 14 FF 00 00 00 00 00 88
14 bytes: 00 04 20 80 7F FF 13 FF 00 00 00 00 00 88
14 bytes: 00 04 20 80 7F FF 12 FF 00 00 00 00 00 88
14 bytes: 00 04 20 80 7F FF 11 FF 00 00 00 00 00 88
^
This Column
Bruce
…On Sun, Jan 22, 2023 at 6:55 PM Russell Taylor ***@***.***> wrote:
You need to convert the hexadecimal vendor and product IDs to decimal and
put in the integers. It was taking your inputs as 0 0.
—
Reply to this email directly, view it on GitHub
<#283 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/ADNLDTTHXL6ZALKOHNSEN6DWTXCF3ANCNFSM6AAAAAAT46TWSQ>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Russell:
I spent a bit more time going through the code and realized that my code
was using the generic joystick class not the Saitex Pro class. I modified
the Saitek Pro class to create an X52 class. I know I am getting into the
class because the debugs in decodevrpn_Saitex_X52: packet of the
verpy_server show up with any motion of the stick. My app however gives a
continuous error message shown below
VRPN Error
(28) from vrpn_Saitek_X52: No response from server for >= 10 seconds
Any thoughts?
Bruce
On Mon, Jan 23, 2023 at 2:15 PM Bruce Clay ***@***.***> wrote:
Russell:
I ran the test as suggested results to follow. The seventh column shown
below is the correct one for the top dial. the values change as I rotate
the dial
G:\SupportLibraries3\vrpn\x64\Debug>vrpn_hid_device_watcher.exe 1699 1884 0
Will accept HID device number 0 that has vendor:product 06a3:075c
HID initialized.
Connected: HID device vendor ID 1699, product ID 1884, aka 06a3:075c
Entering update loop.
14 bytes: 00 04 20 80 7F FF 19 FF 00 00 00 00 00 88
14 bytes: 00 04 20 80 7F FF 18 FF 00 00 00 00 00 88
14 bytes: 00 04 20 80 7F FF 17 FF 00 00 00 00 00 88
14 bytes: 00 04 20 80 7F FF 15 FF 00 00 00 00 00 88
14 bytes: 00 04 20 80 7F FF 14 FF 00 00 00 00 00 88
14 bytes: 00 04 20 80 7F FF 13 FF 00 00 00 00 00 88
14 bytes: 00 04 20 80 7F FF 12 FF 00 00 00 00 00 88
14 bytes: 00 04 20 80 7F FF 11 FF 00 00 00 00 00 88
^
This Column
Bruce
On Sun, Jan 22, 2023 at 6:55 PM Russell Taylor ***@***.***>
wrote:
> You need to convert the hexadecimal vendor and product IDs to decimal and
> put in the integers. It was taking your inputs as 0 0.
>
> —
> Reply to this email directly, view it on GitHub
> <#283 (comment)>, or
> unsubscribe
> <https://github.com/notifications/unsubscribe-auth/ADNLDTTHXL6ZALKOHNSEN6DWTXCF3ANCNFSM6AAAAAAT46TWSQ>
> .
> You are receiving this because you authored the thread.Message ID:
> ***@***.***>
>
#pragma once
#include <stddef.h> // for size_t
#include "vrpn_Analog.h" // for vrpn_Analog
#include "vrpn_BaseClass.h" // for vrpn_BaseClass
#include "vrpn_Button.h" // for vrpn_Button_Filter
#include "vrpn_Configure.h" // for VRPN_CALLBACK, VRPN_USE_HID
#include "vrpn_Connection.h" // for vrpn_CONNECTION_LOW_LATENCY, etc
#include "vrpn_Dial.h" // for vrpn_Dial
#include "vrpn_HumanInterface.h" // for vrpn_HidAcceptor (ptr only), etc
#include "vrpn_Shared.h" // for timeval
#include "vrpn_Types.h" // for vrpn_uint8, vrpn_uint32
#if defined(VRPN_USE_HID)
// Device drivers for the Saitek Controller Raw USB line of products
// Currently supported: ST290 Pro
//
// Exposes three major VRPN device classes: Button, Analog, Dial (as appropriate).
// All models expose Buttons for the keys on the device.
// Button 0 is the programming switch; it is set if the switch is in the "red" position.
//
class vrpn_Saitek_X52_Controller_Raw: public vrpn_BaseClass, protected vrpn_HidInterface {
public:
vrpn_Saitek_X52_Controller_Raw(vrpn_HidAcceptor *filter, const char *name, vrpn_Connection *c = 0,
vrpn_uint16 vendor = 0, vrpn_uint16 product = 0);
virtual ~vrpn_Saitek_X52_Controller_Raw(void);
virtual void mainloop(void) = 0;
protected:
// Set up message handlers, etc.
void init_hid(void);
void on_data_received(size_t bytes, vrpn_uint8 *buffer);
static int VRPN_CALLBACK on_connect(void *thisPtr, vrpn_HANDLERPARAM p);
static int VRPN_CALLBACK on_last_disconnect(void *thisPtr, vrpn_HANDLERPARAM p);
virtual void decodePacket(size_t bytes, vrpn_uint8 *buffer) = 0;
struct timeval _timestamp;
vrpn_HidAcceptor *_filter;
// No actual types to register, derived classes will be buttons, analogs, and/or dials
int register_types(void) { return (0); }
};
class vrpn_Saitek_X52: protected vrpn_Saitek_X52_Controller_Raw, public vrpn_Analog, public vrpn_Button_Filter, public vrpn_Dial
{
public:
vrpn_Saitek_X52(const char *name, vrpn_Connection *c = 0);
virtual ~vrpn_Saitek_X52(void) {};
virtual void mainloop(void);
protected:
// Send report iff changed
void report_changes (vrpn_uint32 class_of_service = vrpn_CONNECTION_LOW_LATENCY);
// Send report whether or not changed
void report (vrpn_uint32 class_of_service = vrpn_CONNECTION_LOW_LATENCY);
void decodePacket(size_t bytes, vrpn_uint8 *buffer);
};
// end of VRPN_USE_HID
#else
class VRPN_API vrpn_Saitek_X52;
#endif
// vrpn_Saitek_X52_Controller_Raw.C: VRPN driver for Saitek Controller Raw devices
#include <stdio.h> // for fprintf, stderr, NULL
#include <string.h> // for memset
#include <math.h> // for sqrt and fabs
#include "vrpn_SaitekX52_Controller_Raw.h"
VRPN_SUPPRESS_EMPTY_OBJECT_WARNING()
#if defined(VRPN_USE_HID)
// USB vendor and product IDs for the models we support
static const vrpn_uint16 SAITEK_VENDOR = 0x06a3;
static const vrpn_uint16 SAITEK_X52 = 0x075c;
static const double POLL_INTERVAL = 1e+6 / 30.0; // If we have not heard, ask.
#define GAMEPAD_TRIGGER_THRESHOLD 30
//////////////////////////////////////////////////////////////////////////
// helpers
//////////////////////////////////////////////////////////////////////////
static vrpn_float64 normalize_dpad(unsigned char up, unsigned char right, unsigned char down, unsigned char left)
{
int x = 0;
int y = 0;
if (right)
{
x += 1;
}
if (left)
{
x -= 1;
}
if (up)
{
y += 1;
}
if (down)
{
y -= 1;
}
size_t index = ((x + 1) * 3) + (y + 1);
vrpn_float64 angles[] = {225, 270, 315, 180, -1, 0, 135, 90, 45};
return (angles[index]);
}
static void normalize_axis(const unsigned int value, const short deadzone, const vrpn_float64 scale, vrpn_float64& channel, int wordSize = 16)
{
channel = (static_cast<float>(value) - (float) (1 << (wordSize - 1)));
if (fabs(channel) < (deadzone * 3 / 4))
{
channel = 0.0f;
}
else
{
channel /= (float) (1 << (wordSize - 1));
}
channel *= scale;
if (channel < -1.0) { channel = -1.0; }
if (channel > 1.0) { channel = 1.0; }
}
static void normalize_axes(const unsigned int x, const unsigned int y, const short deadzone, const vrpn_float64 scale, vrpn_float64& channelX, vrpn_float64& channelY, int wordSize = 16)
{
normalize_axis(x, deadzone, scale, channelX, wordSize);
normalize_axis(y, deadzone, scale, channelY, wordSize);
}
//////////////////////////////////////////////////////////////////////////
// Common base class
//////////////////////////////////////////////////////////////////////////
vrpn_Saitek_X52_Controller_Raw::vrpn_Saitek_X52_Controller_Raw(vrpn_HidAcceptor *filter, const char *name, vrpn_Connection *c,
vrpn_uint16 vendor, vrpn_uint16 product) :
vrpn_BaseClass(name, c), vrpn_HidInterface(filter, vendor, product), _filter(filter)
{
init_hid();
fprintf(stderr, "in vrpn_Saitek_X52_Controller_Raw\n");
}
vrpn_Saitek_X52_Controller_Raw::~vrpn_Saitek_X52_Controller_Raw(void)
{
try {
delete _filter;
} catch (...) {
fprintf(stderr, "vrpn_Saitek_X52_Controller_Raw::~vrpn_Saitek_X52_Controller_Raw(): delete failed\n");
return;
}
}
void vrpn_Saitek_X52_Controller_Raw::init_hid()
{
// Get notifications when clients connect and disconnect
register_autodeleted_handler(d_connection->register_message_type(vrpn_dropped_last_connection), on_last_disconnect, this);
register_autodeleted_handler(d_connection->register_message_type(vrpn_got_connection), on_connect, this);
}
void vrpn_Saitek_X52_Controller_Raw::on_data_received(size_t bytes, vrpn_uint8 *buffer)
{
decodePacket(bytes, buffer);
}
int vrpn_Saitek_X52_Controller_Raw::on_last_disconnect(void* /*thisPtr*/, vrpn_HANDLERPARAM /*p*/)
{
return (0);
}
int vrpn_Saitek_X52_Controller_Raw::on_connect(void* /*thisPtr*/, vrpn_HANDLERPARAM /*p*/)
{
return (0);
}
//////////////////////////////////////////////////////////////////////////
// ST290 Pro Joystick
//////////////////////////////////////////////////////////////////////////
vrpn_Saitek_X52::vrpn_Saitek_X52(const char *name, vrpn_Connection *c) :
vrpn_Saitek_X52_Controller_Raw(new vrpn_HidProductAcceptor(
SAITEK_VENDOR, SAITEK_X52), name, c, SAITEK_VENDOR, SAITEK_X52),
vrpn_Analog(name, c), vrpn_Button_Filter(name, c), vrpn_Dial(name, c)
{
vrpn_Analog::num_channel = 8;
vrpn_Dial::num_dials = 0;
vrpn_Button::num_buttons = 32;
fprintf(stderr, "in vrpn_Saitek_X52 modified contructor\n");
// Initialize the state of all the analogs, buttons, and dials
memset(buttons, 0, sizeof(buttons));
memset(lastbuttons, 0, sizeof(lastbuttons));
memset(channel, 0, sizeof(channel));
memset(last, 0, sizeof(last));
fprintf(stderr, "Saitex X52 Flight system has %d axes and %d buttons.\n",
num_channel, num_buttons);
}
void vrpn_Saitek_X52::mainloop(void)
{
update();
server_mainloop();
struct timeval current_time;
vrpn_gettimeofday(¤t_time, NULL);
if (vrpn_TimevalDuration(current_time, _timestamp) > POLL_INTERVAL)
{
_timestamp = current_time;
report_changes();
// Call the server_mainloop on our unique base class.
server_mainloop();
}
}
void vrpn_Saitek_X52::report(vrpn_uint32 class_of_service)
{
vrpn_Analog::timestamp = _timestamp;
vrpn_Button::timestamp = _timestamp;
if (vrpn_Dial::num_dials > 0)
{
vrpn_Dial::timestamp = _timestamp;
}
vrpn_Analog::report_changes(class_of_service);
vrpn_Button::report_changes();
if (vrpn_Dial::num_dials > 0)
{
vrpn_Dial::report();
}
}
void vrpn_Saitek_X52::report_changes(vrpn_uint32 class_of_service)
{
vrpn_Analog::timestamp = _timestamp;
vrpn_Button::timestamp = _timestamp;
if (vrpn_Dial::num_dials > 0)
{
vrpn_Dial::timestamp = _timestamp;
}
vrpn_Analog::report(class_of_service);
vrpn_Button::report_changes();
if (vrpn_Dial::num_dials > 0)
{
vrpn_Dial::report();
}
}
void vrpn_Saitek_X52::decodePacket(size_t bytes, vrpn_uint8 *buffer)
{
// ST290 Pro joystick
// Decode all full reports, each of which is 6 bytes long.
// Because there is only one type of report, the initial "0" report-type
// byte is removed by the HIDAPI driver.
/*
[0]: X-axis (left=00, right=ff)
[1]: Y-axis - lower byte (up=00, down=ff)
[2]: Z-rotate (left=00, right=ff)
[3]: Slider (up=00, down=ff)
[4]: buttons high nibble "1"=0x10, "2"=0x20, "3"=0x40, "4"=0x80, POV Hat low nibble (none=0x00, N=0x01, NE=0x02, ... NW=0x08)
[5]: 0xf0 high nibble, buttons low nibble (none=0x00, "5"=0x01, "6"=0x02)
*/
// XXX Check to see that this works with HIDAPI, there may be two smaller reports.
fprintf(stderr, "Saitek bytes = %d\n", bytes);
if (bytes >= 6)
{
for (int index = 0; index < bytes; index++)
{
fprintf(stderr, "%d ", buffer[index]);
}
fprintf(stderr, "\n");
normalize_axes(buffer[0], buffer[1], 0x08, 1.0f, channel[0], channel[1], 8);
normalize_axis(buffer[2], 0x08, 1.0f, channel[2], 8);
normalize_axis(buffer[3], 0x08, 1.0f, channel[3], 8);
normalize_axis(buffer[4], 0x08, 1.0f, channel[4], 8); // throttle
normalize_axis(buffer[5], 0x08, 1.0f, channel[5], 8); // side dial
normalize_axis(buffer[6], 0x08, 1.0f, channel[6], 8); // top dial
normalize_axis(buffer[7], 0x08, 1.0f, channel[7], 8); // slider
vrpn_uint8 value, mask;
value = (buffer[4] >> 4);
for (int btn = 0; btn < 4; btn++)
{
mask = static_cast<vrpn_uint8>(1 << (btn % 8));
buttons[btn] = ((value & mask) != 0);
}
value = (buffer[5] & 0x0f);
for (int btn = 0; btn < 4; btn++)
{
mask = static_cast<vrpn_uint8>(1 << (btn % 8));
buttons[btn + 4] = ((value & mask) != 0);
}
// Point of View Hat
buttons[6] = buttons[7] = buttons[8] = buttons[9] = 0;
switch (buffer[4] & 0x0f)
{
case 1: // up
buttons[6] = true;
break;
case 2:
buttons[6] = buttons[7] = true;
break;
case 3: // right
buttons[7] = true;
break;
case 4:
buttons[7] = buttons[8] = true;
break;
case 5: // down
buttons[8] = true;
break;
case 6:
buttons[8] = buttons[9] = true;
break;
case 7: // left
buttons[9] = true;
break;
case 8:
buttons[9] = buttons[6] = true;
break;
case 0:
default:
// nothing to do
break;
}
// channel[4] = normalize_dpad(buttons[7], buttons[8], buttons[9], buttons[10]);
}
else
{
fprintf(stderr, "vrpn_Saitek_X52: Found a corrupted report; # total bytes = %u\n", static_cast<unsigned>(bytes));
}
}
// End of VRPN_USE_HID
#endif
|
Those errors come when the name of the device on the client side does not match the name of the device on the server side. It looks like it is named something like vrpn_Saitek_X52@localhost on the client side. The server configuration file (or whatever code you are using to generate the server) is picking another name. The ones in the config file are things like saitek0. If that's what you're using, then the client-side name should change. You should be able to use vrpn_print_devices to print it in addition to your custom client code if you'd prefer. |
I am using an Saitek X52 control system not the Pro version. I can see a change in the X52 HOTA properties app when I rotate the top knob on the throttle but the change does not show up in VRPN. AnalogDataChanged does not report a change when the knob is moved.
The text was updated successfully, but these errors were encountered: