As Tony mentioned, UDS is not easy to understand and it's not documented anywhere but it's got more enhanced data than standard OBD2 PIDs - that's why can sniffing individual PID's is the best option IMO to find what you are looking for over UDS.
This is my setup to do CAN sniffing:

1. HEX V2 used with VCDS to send requests and see realtime data
2. Teensy 3.2 + TI sn65hvd230 CAN transceiver placed in an OBD2 connector
3. Y splitter that connects to both devices to the cars OBD port
I use a simple sketch on the Teensy that print's every CAN frame it see's (from either end: car or VCDS) onto the Arduino serial monitor. I then log each frame (request/response) and value.
I must note, on the PQ25 you most probably have a Bosch MED ECU and it's not as fast as the MQBs Continental Simos, so request poll speed might have to be lower in your case.
I know for a fact that the MQB UDS requests don't work with the PQ35 platform as I have plugged my device into a VW Scirocco and it failed to return any positive UDS responses.
For the Teensy 4 I used the TI sn65hvd230 CAN transceiver on CAN1 (pins 22 and 23).
Here is a simple sketch that will request engine coolant temp and save it into a variable and print it out in your serial monitor:
Code:
#include <FlexCAN_T4.h>
FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can0;
int engineCoolantTemp = 0; // Save the data here
void setup(){
while (!Serial && millis() < 5000);
Serial.begin(115200);
Can0.begin();
Can0.setBaudRate(500000);
Can0.setMBFilter(REJECT_ALL);
Can0.setMBFilter(MB1, 0x7E8);
Can0.enableMBInterrupt(MB1);
Can0.onReceive(canSniff);
}
const long loopDelay1 = 500; // Make a request every 500ms
unsigned long timeNow1 = 0;
void loop(){
Can0.events();
if (millis() > timeNow1 + loopDelay1)
{
timeNow1 = millis();
canTx_OBD(); // Query data from CAN BUS via OBD
//canTx_UDS(); // Query data from CAN BUS via UDS
}
}
void canSniff (const CAN_message_t &msg){ // Global callback to catch any CAN frame coming in
Serial.println(" ");
Serial.print("MB: ");
Serial.print(msg.mb);
Serial.print(" ID: 0x");
Serial.print(msg.id, HEX);
Serial.print(" EXT: ");
Serial.print(msg.flags.extended);
Serial.print(" LEN: ");
Serial.print(msg.len);
Serial.print(" DATA: ");
for (uint8_t i = 0; i < 8; i++)
{
Serial.print(msg.buf[i], HEX);
Serial.print(" ");
}
if (msg.buf[1] == 0x01 && msg.buf[2] == 0x05){
engineCoolantTemp = msg.buf[3] - 40;
Serial.println(" ");
Serial.printf("OBD: Engine Coolant Temp: %d c", engineCoolantTemp);
}
if (msg.buf[1] == 0x22 && msg.buf[2] == 0xf4 && msg.buf[3] == 0x05){
engineCoolantTemp = msg.buf[4] - 40;
Serial.println(" ");
Serial.printf("UDS: Engine Coolant Temp: %d c", engineCoolantTemp);
}
}
void canTx_OBD(){ // Request function to ask for Engine Coolant Temp via OBD request
CAN_message_t msgTx, msgRx;
msgTx.buf[0] = 0x02;
msgTx.buf[1] = 0x01;
msgTx.buf[2] = 0x05;
msgTx.buf[3] = 0;
msgTx.buf[4] = 0;
msgTx.buf[5] = 0;
msgTx.buf[6] = 0;
msgTx.buf[7] = 0;
msgTx.len = 8; // number of bytes in request
msgTx.flags.extended = 0; // 11 bit header, not 29 bit
msgTx.flags.remote = 0;
msgTx.id = 0x7E0; // request header for OBD
Can0.write(msgTx);
Serial.println("canTx_OBD REQ sent");
}
void canTx_UDS(){ // Request function to ask for Engine Coolant Temp via UDS request
CAN_message_t msgTx, msgRx;
msgTx.buf[0] = 0x03;
msgTx.buf[1] = 0x22;
msgTx.buf[2] = 0xf4;
msgTx.buf[3] = 0x05;
msgTx.buf[4] = 0;
msgTx.buf[5] = 0;
msgTx.buf[6] = 0;
msgTx.buf[7] = 0;
msgTx.len = 8; // number of bytes in request
msgTx.flags.extended = 0; // 11 bit header, not 29 bit
msgTx.flags.remote = 0;
msgTx.id = 0x7E0; // request header for OBD
Can0.write(msgTx);
Serial.println("canTx_UDS REQ sent");
}
You can try first the OBD method of request, then you can try the UDS method by commenting out the OBD request and uncommenting the UDS request in the main loop.