From 890138cf7c1cd6abfd6b850a6aae370bfcdc6287 Mon Sep 17 00:00:00 2001 From: Asaf Wexler Date: Tue, 11 Nov 2025 00:22:07 +0200 Subject: [PATCH 1/2] Fix qMakePair API for Qt 6 compatibility Replace qMakePair() calls with QPair() constructor Qt 6 requires rvalue references for qMakePair arguments --- wrappers/Qt/EventLogger.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/wrappers/Qt/EventLogger.cpp b/wrappers/Qt/EventLogger.cpp index 7f423eb..927843c 100644 --- a/wrappers/Qt/EventLogger.cpp +++ b/wrappers/Qt/EventLogger.cpp @@ -276,7 +276,7 @@ void DSEventLogger::saveDataLoop() void DSEventLogger::onCANUsageChanged(int usage) { Q_UNUSED(usage); - m_canUsageLog.append(qMakePair(currentTime(), usage)); + m_canUsageLog.append(QPair(currentTime(), usage)); } /** @@ -285,7 +285,7 @@ void DSEventLogger::onCANUsageChanged(int usage) void DSEventLogger::onCPUUsageChanged(int usage) { Q_UNUSED(usage); - m_cpuUsageLog.append(qMakePair(currentTime(), usage)); + m_cpuUsageLog.append(QPair(currentTime(), usage)); } /** @@ -294,7 +294,7 @@ void DSEventLogger::onCPUUsageChanged(int usage) void DSEventLogger::onRAMUsageChanged(int usage) { Q_UNUSED(usage); - m_ramUsageLog.append(qMakePair(currentTime(), usage)); + m_ramUsageLog.append(QPair(currentTime(), usage)); } /** @@ -303,7 +303,7 @@ void DSEventLogger::onRAMUsageChanged(int usage) void DSEventLogger::onNewMessage(QString message) { Q_UNUSED(message); - m_messagesLog.append(qMakePair(currentTime(), message)); + m_messagesLog.append(QPair(currentTime(), message)); } /** @@ -312,7 +312,7 @@ void DSEventLogger::onNewMessage(QString message) void DSEventLogger::onDiskUsageChanged(int usage) { Q_UNUSED(usage); - m_diskUsageLog.append(qMakePair(currentTime(), usage)); + m_diskUsageLog.append(QPair(currentTime(), usage)); } /** @@ -321,7 +321,7 @@ void DSEventLogger::onDiskUsageChanged(int usage) void DSEventLogger::onEnabledChanged(bool enabled) { LOG << "Robot enabled state set to" << enabled; - m_enabledLog.append(qMakePair(currentTime(), enabled)); + m_enabledLog.append(QPair(currentTime(), enabled)); } /** @@ -338,7 +338,7 @@ void DSEventLogger::onTeamNumberChanged(int number) void DSEventLogger::onVoltageChanged(float voltage) { Q_UNUSED(voltage); - m_voltageLog.append(qMakePair(currentTime(), voltage)); + m_voltageLog.append(QPair(currentTime(), voltage)); } /** @@ -347,7 +347,7 @@ void DSEventLogger::onVoltageChanged(float voltage) void DSEventLogger::onRobotCodeChanged(bool robotCode) { LOG << "Robot code status set to" << robotCode; - m_robotCodeLog.append(qMakePair(currentTime(), robotCode)); + m_robotCodeLog.append(QPair(currentTime(), robotCode)); } /** @@ -356,7 +356,7 @@ void DSEventLogger::onRobotCodeChanged(bool robotCode) void DSEventLogger::onFMSCommunicationsChanged(bool connected) { LOG << "FMS communications set to" << connected; - m_fmsCommsLog.append(qMakePair(currentTime(), connected)); + m_fmsCommsLog.append(QPair(currentTime(), connected)); } /** @@ -365,7 +365,7 @@ void DSEventLogger::onFMSCommunicationsChanged(bool connected) void DSEventLogger::onRadioCommunicationsChanged(bool connected) { LOG << "Radio communications set to" << connected; - m_radioCommsLog.append(qMakePair(currentTime(), connected)); + m_radioCommsLog.append(QPair(currentTime(), connected)); } /** @@ -374,7 +374,7 @@ void DSEventLogger::onRadioCommunicationsChanged(bool connected) void DSEventLogger::onRobotCommunicationsChanged(bool connected) { LOG << "Robot communications set to" << connected; - m_robotCommsLog.append(qMakePair(currentTime(), connected)); + m_robotCommsLog.append(QPair(currentTime(), connected)); } /** @@ -383,7 +383,7 @@ void DSEventLogger::onRobotCommunicationsChanged(bool connected) void DSEventLogger::onEmergencyStoppedChanged(bool emergencyStopped) { LOG << "ESTOP set to" << emergencyStopped; - m_emergencyStopLog.append(qMakePair(currentTime(), emergencyStopped)); + m_emergencyStopLog.append(QPair(currentTime(), emergencyStopped)); } /** @@ -392,7 +392,7 @@ void DSEventLogger::onEmergencyStoppedChanged(bool emergencyStopped) void DSEventLogger::onControlModeChanged(DriverStation::Control mode) { LOG << "Robot control mode set to" << mode; - m_controlModeLog.append(qMakePair(currentTime(), (int)mode)); + m_controlModeLog.append(QPair(currentTime(), (int)mode)); } /** From 9d3fa9411c0501beb7670a726d9cc16b774d9dc9 Mon Sep 17 00:00:00 2001 From: Asaf Wexler Date: Mon, 26 Jan 2026 22:21:41 +0200 Subject: [PATCH 2/2] Fix WPILib 2026 protocol compatibility This fixes crashes when using QDriverStation with WPILib 2026 simulation. Changes: - Fix joystick size calculation to not include length byte in the count - Fix button encoding to use variable-length bytes ((numButtons+7)/8) instead of always sending 2 bytes - Add WPILib HAL limits (12 axes, 32 buttons, 12 POVs) to prevent protocol errors with controllers that exceed these limits - Support up to 32 buttons (was limited to 16) The crash was caused by WPILib's DecodeUDP receiving incorrect packet sizes, leading to invalid memory access. Co-Authored-By: Claude Opus 4.5 --- src/joysticks.c | 22 +++++++++++++---- src/protocols/frc_2020.c | 52 +++++++++++++++++++++++++++------------- 2 files changed, 53 insertions(+), 21 deletions(-) diff --git a/src/joysticks.c b/src/joysticks.c index 7b1861e..040d806 100644 --- a/src/joysticks.c +++ b/src/joysticks.c @@ -215,10 +215,19 @@ void DS_JoysticksReset(void) register_event(); } +/* + * WPILib HAL joystick limits + */ +#define WPILIB_MAX_AXES 12 +#define WPILIB_MAX_BUTTONS 32 +#define WPILIB_MAX_POVS 12 + /** * Registers a new joystick with the given number of \a axes, \a hats and * \a buttons. All joystick values are set to a neutral state to ensure * safe operation of the robot. + * + * Values are clamped to WPILib limits to prevent protocol errors. */ void DS_JoysticksAdd(const int axes, const int hats, const int buttons) { @@ -229,13 +238,18 @@ void DS_JoysticksAdd(const int axes, const int hats, const int buttons) return; } + /* Clamp to WPILib limits */ + int clamped_axes = (axes > WPILIB_MAX_AXES) ? WPILIB_MAX_AXES : axes; + int clamped_hats = (hats > WPILIB_MAX_POVS) ? WPILIB_MAX_POVS : hats; + int clamped_buttons = (buttons > WPILIB_MAX_BUTTONS) ? WPILIB_MAX_BUTTONS : buttons; + /* Allocate memory for a new joystick */ DS_Joystick *joystick = (DS_Joystick *)calloc(1, sizeof(DS_Joystick)); - /* Set joystick properties */ - joystick->num_axes = axes; - joystick->num_hats = hats; - joystick->num_buttons = buttons; + /* Set joystick properties (clamped to WPILib limits) */ + joystick->num_axes = clamped_axes; + joystick->num_hats = clamped_hats; + joystick->num_buttons = clamped_buttons; /* Set joystick value arrays */ joystick->hats = calloc(hats, sizeof(int)); diff --git a/src/protocols/frc_2020.c b/src/protocols/frc_2020.c index db45e48..478ae58 100644 --- a/src/protocols/frc_2020.c +++ b/src/protocols/frc_2020.c @@ -280,18 +280,32 @@ static uint8_t get_station_code(void) } /** - * Returns the size of the given \a joystick. This function is used to generate - * joystick data (which is sent to the robot) and to resize the client->robot - * datagram automatically. + * Returns the number of bytes needed to encode the button bitmask. + * WPILib expects (numButtons + 7) / 8 bytes. + */ +static int get_button_bytes(const int num_buttons) +{ + if (num_buttons <= 0) + return 0; + return (num_buttons + 7) / 8; +} + +/** + * Returns the size of the given \a joystick data block (NOT including the + * length byte itself). This function is used to generate joystick data + * (which is sent to the robot) and to resize the client->robot datagram. + * + * Format: [tag][axes_count][axes...][button_count][button_bytes...][pov_count][povs...] */ static uint8_t get_joystick_size(const int joystick) { - int header_size = 2; - int button_data = DS_GetJoystickNumButtons(joystick) + 1; - int axis_data = DS_GetJoystickNumAxes(joystick) + 1; - int hat_data = (DS_GetJoystickNumHats(joystick) * 2) + 1; + int tag_size = 1; /* Tag byte (0x0c) - included in length */ + int num_buttons = DS_GetJoystickNumButtons(joystick); + int button_data = 1 + get_button_bytes(num_buttons); /* count + bitmask bytes */ + int axis_data = 1 + DS_GetJoystickNumAxes(joystick); /* count + axis bytes */ + int hat_data = 1 + (DS_GetJoystickNumHats(joystick) * 2); /* count + 2 bytes per hat */ - return header_size + button_data + axis_data + hat_data; + return tag_size + axis_data + button_data + hat_data; } /** @@ -383,16 +397,20 @@ static DS_String get_joystick_data(void) for (j = 0; j < DS_GetJoystickNumAxes(i); ++j) DS_StrAppend(&data, DS_FloatToByte(DS_GetJoystickAxis(i, j), 1)); - /* Generate button data */ - uint16_t button_flags = 0; - for (j = 0; j < DS_GetJoystickNumButtons(i); ++j) - button_flags += DS_GetJoystickButton(i, j) ? (int)pow(2, j) : 0; + /* Generate button bitmask (supports up to 32 buttons) */ + int num_buttons = DS_GetJoystickNumButtons(i); + uint32_t button_flags = 0; + for (j = 0; j < num_buttons; ++j) + if (DS_GetJoystickButton(i, j)) + button_flags |= (1u << j); + + /* Add button count */ + DS_StrAppend(&data, (uint8_t)num_buttons); - /* Add button data */ - /* potential TODO: this assumes num_buttons <= 16 */ - DS_StrAppend(&data, DS_GetJoystickNumButtons(i)); - DS_StrAppend(&data, (uint8_t)(button_flags >> 8)); - DS_StrAppend(&data, (uint8_t)(button_flags)); + /* Add button bitmask bytes in big-endian order (high bytes first) */ + int button_bytes = get_button_bytes(num_buttons); + for (j = button_bytes - 1; j >= 0; --j) + DS_StrAppend(&data, (uint8_t)(button_flags >> (j * 8))); /* Add hat data */ DS_StrAppend(&data, DS_GetJoystickNumHats(i));