This document provides comprehensive analysis of USB connection patterns across u-blox device families and the architectural implications for the ROS2 driver. This analysis is based on actual testing and implementation experience with real devices.
USB Device: 1546:01a9
├── Interface 0 (CDC Control Interface)
│ ├── Class: 2 (Communications)
│ ├── SubClass: 2 (Abstract Control Model)
│ ├── Protocol: 1 (AT-commands)
│ └── Endpoint 0x83: Interrupt IN (Status notifications)
└── Interface 1 (CDC Data Interface)
├── Class: 10 (CDC Data)
├── SubClass: 0
├── Protocol: 0
├── Endpoint 0x81: Bulk IN (Data RX)
└── Endpoint 0x02: Bulk OUT (Data TX)
Verified Characteristics:
- Interface Count: 2 (Control + Data) - CRITICAL: Code assumes this
- USB Class: CDC-ACM (Communications Device Class)
- Serial String (iSerial): Typically NOT factory programmed - returns
LIBUSB_ERROR_INVALID_PARAM - Connection Pattern: Single USB device, both interfaces must be claimed
- Control Setup: Requires CDC-ACM line state control (
0x21, 0x22, DTR|RTS) - Driver Compatibility: Works with Linux cdc-acm kernel driver
- Serial Programming: May need u-center programming for custom serial strings
Key Concept: X20P presents as THREE SEPARATE USB DEVICES on the same physical module:
- 0x01ab: F9P/F9R-compatible CDC-ACM interface (works identically to F9P/F9R)
- 0x050c: Vendor-specific UART1 interface
- 0x050d: Vendor-specific UART2 interface
USB Device 1: 1546:050c (UART1)
Device Descriptor:
iSerial: 3 [Factory ID] (Factory programmed, unique per device)
└── Interface 0 (Vendor Specific)
├── Class: 255 (Vendor Specific)
├── SubClass: 255
├── Protocol: 255
├── bNumEndpoints: 2
├── Endpoint 0x81: Bulk IN (Data RX)
└── Endpoint 0x02: Bulk OUT (Data TX)
USB Device 2: 1546:050d (UART2)
Device Descriptor:
iSerial: 3 [Factory ID] (Same factory serial as UART1)
└── Interface 0 (Vendor Specific)
├── Class: 255 (Vendor Specific)
├── SubClass: 255
├── Protocol: 255
├── bNumEndpoints: 2
├── Endpoint 0x81: Bulk IN (Data RX)
└── Endpoint 0x02: Bulk OUT (Data TX)
USB Device 3: 1546:01ab (General Interface)
Device Descriptor:
iSerial: 0 (u-center programmable, not factory set)
├── Interface 0 (CDC Control Interface)
│ ├── Class: 2 (Communications)
│ ├── SubClass: 2 (Abstract Control Model)
│ ├── Protocol: 1 (AT-commands)
│ └── Endpoint 0x83: Interrupt IN (Status notifications)
└── Interface 1 (CDC Data Interface)
├── Class: 10 (CDC Data)
├── SubClass: 0
├── Protocol: 0
├── Endpoint 0x81: Bulk IN (Data RX)
└── Endpoint 0x02: Bulk OUT (Data TX)
Verified Characteristics:
- Physical Module: Single X20P device presents as three separate USB devices
- USB Device 1 (0x01ab): Identical to F9P/F9R architecture
- Interface Count: 2 (CDC Control + CDC Data)
- USB Class: CDC-ACM (Communications Device Class)
- Serial String: u-center programmed (like F9P devices)
- Control Setup: Requires CDC-ACM control (
0x21, 0x22, DTR|RTS) - Driver Behavior: Same as F9P/F9R, no special handling needed
- USB Device 2&3 (0x050c/0x050d): Vendor-specific UART interfaces (NOT SUPPORTED)
- Interface Count: 1 per device (single vendor-specific interface)
- USB Class: 255 (Vendor Specific) - No CDC-ACM protocol
- Serial String: Factory programmed and reliable
- Control Setup: NO CDC-ACM control - causes
LIBUSB_ERROR_PIPE - Status: Currently unsupported - see GitHub Issue #48
- Priority: 0x01ab listed first in device family (F9P-compatible gets priority)
| Device Family | Interface Count | USB Class | Control Setup | iSerial Status | Product IDs |
|---|---|---|---|---|---|
| F9P | 2 (Control+Data) | CDC-ACM (2/10) | CDC line state required | u-center programmed | 0x01a9 |
| F9R | 2 (Control+Data) | CDC-ACM (2/10) | CDC line state required | u-center programmed | 0x01a9 |
| X20P (F9P-compatible) | 2 (Control+Data) | CDC-ACM (2/10) | CDC line state required | u-center programmed | 0x01ab (priority) |
| X20P (UART1/UART2) | 1 (Combined) | Vendor (255) | No control transfers | Factory programmed | 0x050c, 0x050d |
Note: X20P presents as three separate USB devices. The 0x01ab interface is architecturally identical to F9P/F9R.
rc = libusb_get_string_descriptor_ascii(devHandle, desc.iSerialNumber,
reinterpret_cast<unsigned char *>(serial_num_string),
SERIAL_STRING_BUFFER_SIZE);
// Expected: rc = LIBUSB_ERROR_INVALID_PARAM (if not u-center programmed)
// Behavior: Code continues if LIBUSB_ERROR_INVALID_PARAM, buffer contains undefined data
// Matching: If no serial specified (empty), uses first device; if serial specified, matching fails
// Code: reliable_iserial = false (LIBUSB_ERROR_INVALID_PARAM expected and handled)rc = libusb_get_string_descriptor_ascii(devHandle, desc.iSerialNumber,
reinterpret_cast<unsigned char *>(serial_num_string),
SERIAL_STRING_BUFFER_SIZE);
// Expected: rc >= 0, serial_buf = factory programmed string (e.g., unique device ID)
// Behavior: Factory programs reliable iSerial for each device
// Code: reliable_iserial = true (error if read fails)
// Usage: Reliable device identification and UART differentiationrc = libusb_get_string_descriptor_ascii(devHandle, desc.iSerialNumber,
reinterpret_cast<unsigned char *>(serial_num_string),
SERIAL_STRING_BUFFER_SIZE);
// Expected: rc = LIBUSB_ERROR_INVALID_PARAM (if not user-programmed)
// Behavior: F9P-style, requires u-center programming (e.g., "DBENI0OW", "DBENI0P4")
// Code: reliable_iserial = false (LIBUSB_ERROR_INVALID_PARAM expected if empty)
// Usage: User-configured device identification// 256-byte buffer ensures full serial string capture (8-9 characters + overhead)
char serial_num_string[SERIAL_STRING_BUFFER_SIZE];
// libusb call uses constant size rather than sizeof() to handle function parameters
rc = libusb_get_string_descriptor_ascii(devHandle, desc.iSerialNumber,
reinterpret_cast<unsigned char *>(serial_num_string),
SERIAL_STRING_BUFFER_SIZE);auto device_info = ublox_dgnss::get_device_family_info(device_family_);
uint8_t expected_interfaces;
if (device_family_ == ublox_dgnss::DeviceFamily::X20P) {
// X20P: Architecture depends on which USB device we're connecting to
if (dev_desc.idProduct == 0x01ab) {
// 0x01ab: F9P/F9R-compatible CDC-ACM interface (IDENTICAL to F9P/F9R)
expected_interfaces = 2;
} else {
// 0x050c/0x050d: Vendor-specific UART interfaces (DIFFERENT from F9P/F9R)
expected_interfaces = 1;
}
} else {
// F9P/F9R: CDC-ACM architecture (2 interfaces: Control + Data)
expected_interfaces = 2;
}
if (num_interfaces_ != expected_interfaces) {
throw std::string("Interface count mismatch for ") + device_info.name +
": expected " + std::to_string(expected_interfaces) +
", got " + std::to_string(num_interfaces_);
}// Product ID-based CDC control determination
bool use_cdc_control = false;
if (device_family_ == ublox_dgnss::DeviceFamily::X20P) {
// X20P: CDC control ONLY for F9P-compatible interface (0x01ab)
use_cdc_control = (dev_desc.idProduct == 0x01ab);
} else {
// F9P/F9R: Always use CDC-ACM control (standard behavior)
use_cdc_control = true;
}
if (use_cdc_control) {
// CDC-ACM line state setup for F9P/F9R and X20P 0x01ab
rc = libusb_control_transfer(devh_, 0x21, 0x22, ACM_CTRL_DTR | ACM_CTRL_RTS, 0, NULL, 0, 0);
if (rc < 0 && rc != LIBUSB_ERROR_BUSY) {
throw libusb_error_name(rc);
}
}
// Note: X20P 0x050c/0x050d devices don't need CDC control transfersswitch (interface_desc->bInterfaceClass) {
case LIBUSB_CLASS_COMM: // F9P/F9R Control interface
ep_comms_in_addr_ = interface_desc->endpoint[0].bEndpointAddress;
break;
case LIBUSB_CLASS_DATA: // F9P/F9R Data interface
ep_data_out_addr_ = interface_desc->endpoint[0].bEndpointAddress;
ep_data_in_addr_ = interface_desc->endpoint[1].bEndpointAddress;
break;
case 255: // X20P Vendor-specific class
// Dynamic endpoint discovery for vendor-specific devices
for (uint8_t ep = 0; ep < interface_desc->bNumEndpoints; ep++) {
const struct libusb_endpoint_descriptor* ep_desc = &interface_desc->endpoint[ep];
if ((ep_desc->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK) {
if (ep_desc->bEndpointAddress & LIBUSB_ENDPOINT_IN) {
ep_data_in_addr_ = ep_desc->bEndpointAddress;
} else {
ep_data_out_addr_ = ep_desc->bEndpointAddress;
}
}
}
break;
default:
throw std::string("Unsupported bInterfaceClass: ") + std::to_string(interface_desc->bInterfaceClass);
}struct DeviceFamilyInfo {
std::string name; // Short name: "F9P", "F9R", "X20P"
std::vector<uint16_t> product_ids; // USB product IDs (multiple for X20P)
std::string description; // Full description for logging
bool sensor_fusion_capable; // F9R wheel tick and ESF support
bool reliable_iserial; // X20P has reliable iSerial, F9 may not
bool dual_uart_capable; // X20P has dual UART support
// Helper methods
uint16_t primary_product_id() const { return product_ids.empty() ? 0 : product_ids[0]; }
bool has_product_id(uint16_t pid) const;
};const std::map<DeviceFamily, DeviceFamilyInfo> DEVICE_FAMILY_MAP = {
{DeviceFamily::F9P, {
"F9P",
{0x01a9}, // Single product ID
"F9P - High-precision GNSS",
false, // sensor_fusion_capable
false, // reliable_iserial (may need u-center programming)
false // dual_uart_capable
}},
{DeviceFamily::F9R, {
"F9R",
{0x01a9}, // Single product ID
"F9R - High-precision GNSS with sensor fusion",
true, // sensor_fusion_capable (wheel ticks, ESF)
false, // reliable_iserial (may need u-center programming)
false // dual_uart_capable
}},
{DeviceFamily::X20P, {
"X20P",
{0x01ab, 0x050c, 0x050d}, // THREE separate USB devices: F9P-compatible + UART1 + UART2
"X20P - All-band GNSS (multiple interfaces)",
false, // sensor_fusion_capable
true, // reliable_iserial (factory set for 0x050c/0x050d, user-programmed for 0x01ab)
true // dual_uart_capable
}}
};bool Connection::open_device() {
// ... device opening logic ...
// Get actual interface count from device
num_interfaces_ = conf_desc->bNumInterfaces;
// Product ID-based interface validation for X20P three-device architecture
auto device_info = ublox_dgnss::get_device_family_info(device_family_);
uint8_t expected_interfaces;
if (device_family_ == ublox_dgnss::DeviceFamily::X20P) {
// X20P: Architecture depends on which USB device we're connecting to
if (dev_desc.idProduct == 0x01ab) {
// 0x01ab: F9P/F9R-compatible CDC-ACM interface (IDENTICAL to F9P/F9R)
expected_interfaces = 2;
} else {
// 0x050c/0x050d: Vendor-specific UART interfaces (DIFFERENT from F9P/F9R)
expected_interfaces = 1;
}
} else {
// F9P/F9R: CDC-ACM architecture (2 interfaces: Control + Data)
expected_interfaces = 2;
}
if (num_interfaces_ != expected_interfaces) {
throw std::string("Interface count mismatch for ") + device_info.name +
": expected " + std::to_string(expected_interfaces) +
", got " + std::to_string(num_interfaces_);
}
// Claim interfaces based on actual count
for (uint8_t if_num = 0; if_num < num_interfaces_; if_num++) {
if (libusb_kernel_driver_active(devh_, if_num)) {
libusb_detach_kernel_driver(devh_, if_num);
}
rc = libusb_claim_interface(devh_, if_num);
if (rc < 0) {
throw std::string("Error claiming interface ") + std::to_string(if_num) +
" for " + device_info.name + ": " + libusb_error_name(rc);
}
}
// ... rest of connection logic ...
}// Multi-class endpoint discovery for all device families
for (uint8_t i = 0; i < num_interfaces_; i++) {
auto interface = &conf_desc->interface[i];
for (uint8_t j = 0; j < interface->num_altsetting; j++) {
auto interface_desc = &interface->altsetting[j];
switch (interface_desc->bInterfaceClass) {
case LIBUSB_CLASS_COMM:
// CDC-ACM Control interface - F9P/F9R and X20P 0x01ab
ep_comms_in_addr_ = interface_desc->endpoint[0].bEndpointAddress;
break;
case LIBUSB_CLASS_DATA:
// CDC-ACM Data interface - F9P/F9R and X20P 0x01ab
ep_data_out_addr_ = interface_desc->endpoint[0].bEndpointAddress;
ep_data_in_addr_ = interface_desc->endpoint[1].bEndpointAddress;
break;
case 255: // LIBUSB_CLASS_VENDOR_SPEC
// X20P vendor-specific interfaces (0x050c/0x050d)
for (uint8_t ep = 0; ep < interface_desc->bNumEndpoints; ep++) {
const struct libusb_endpoint_descriptor* ep_desc = &interface_desc->endpoint[ep];
if ((ep_desc->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK) {
if (ep_desc->bEndpointAddress & LIBUSB_ENDPOINT_IN) {
ep_data_in_addr_ = ep_desc->bEndpointAddress;
} else {
ep_data_out_addr_ = ep_desc->bEndpointAddress;
}
}
}
break;
default:
throw std::string("Error unknown bInterfaceClass: ") + std::to_string(interface_desc->bInterfaceClass);
}
}
}// Enhanced serial string reading with device family awareness
rc = libusb_get_string_descriptor_ascii(devHandle, desc.iSerialNumber,
reinterpret_cast<unsigned char *>(serial_num_string),
SERIAL_STRING_BUFFER_SIZE);
// Product ID-based reliability determination
bool reliable_iserial = false;
if (device_family_ == ublox_dgnss::DeviceFamily::X20P) {
if (desc.idProduct == 0x01ab) {
reliable_iserial = false; // 0x01ab: F9P/F9R-compatible (user-programmed)
} else {
reliable_iserial = true; // 0x050c/0x050d: Factory programmed
}
} else {
reliable_iserial = device_info.reliable_iserial; // F9P/F9R: false
}
if (rc < 0) {
if (reliable_iserial) {
// Factory iSerial should be reliable (X20P 0x050c/0x050d)
throw std::string("Serial string read failed (unexpected): ") + libusb_error_name(rc);
} else {
// User-programmed iSerial may not be reliable (F9P/F9R + X20P 0x01ab)
if (rc != LIBUSB_ERROR_INVALID_PARAM) {
throw std::string("Serial string read failed: ") + libusb_error_name(rc);
}
// LIBUSB_ERROR_INVALID_PARAM is expected if serial not programmed
// Note: Code does NOT set empty string, buffer contains undefined data
}
}rc = libusb_claim_interface(devh_, if_num);
if (rc < 0) {
throw std::string("Error claiming interface ") + std::to_string(if_num) +
" for " + device_info.name + ": " + libusb_error_name(rc);
}
// Actual error handling is simple - just throws with libusb_error_nameThe system registers hotplug callbacks for all product IDs in the device family:
1. Device Detection (1546:01a9)
2. Open USB Device
3. Get Configuration Descriptor
4. Validate: 2 interfaces expected
5. Claim Interface 0 (CDC Control)
6. Claim Interface 1 (CDC Data)
7. Discover CDC-ACM Endpoints
8. Setup Transfers on Data Interface
9. Begin Communication
Critical Understanding: X20P presents three separate USB devices from one physical module. Each requires different handling:
1. Device Detection (1546:01ab) - Uses F9P/F9R code path
2. Open USB Device
3. Get Configuration Descriptor
4. Validate: 2 interfaces expected (Control + Data) - SAME AS F9P/F9R
5. Claim Interface 0 (CDC Control) and Interface 1 (CDC Data) - SAME AS F9P/F9R
6. Discover CDC-ACM Endpoints (Class 2/10) - SAME AS F9P/F9R
7. Setup CDC Line State Control (DTR|RTS) - SAME AS F9P/F9R
8. Setup Transfers on Data Interface - SAME AS F9P/F9R
9. Begin Communication - SAME AS F9P/F9R
1. Device Detection (1546:050c or 1546:050d) - Special X20P handling
2. Open USB Device
3. Get Configuration Descriptor
4. Validate: 1 interface expected - DIFFERENT FROM F9P/F9R
5. Claim Interface 0 (Vendor Specific) - DIFFERENT FROM F9P/F9R
6. Discover Vendor-Specific Endpoints (Class 255) - DIFFERENT FROM F9P/F9R
7. Setup Transfers on Single Interface - NO CDC CONTROL
8. Begin Communication - NO CDC CONTROL TRANSFERS
1. Device Detection (1546:01a9)
2. Open USB Device Handle
3. Get Configuration Descriptor
4. Validate: 2 interfaces required
5. Claim Interface 0 (CDC Control)
6. Claim Interface 1 (CDC Data)
7. Execute CDC-ACM Line State Control (0x21, 0x22)
8. Discover CDC Class-Specific Endpoints
9. Setup USB Transfers on Data Interface
10. Begin UBX Protocol Communication
1. Device Detection (1546:01ab) - Uses F9P/F9R code path
2. Open USB Device Handle
3. Get Configuration Descriptor
4. Validate: 2 interfaces required (Control + Data)
5. Claim Interface 0 (CDC Control) and Interface 1 (CDC Data)
6. Execute CDC-ACM Line State Control (0x21, 0x22)
7. Discover CDC Class-Specific Endpoints
8. Setup USB Transfers on Data Interface
9. Begin UBX Protocol Communication
1. Device Detection (1546:050c or 1546:050d)
2. Open USB Device Handle
3. Get Configuration Descriptor
4. Validate: 1 interface required
5. Claim Interface 0 (Vendor Specific)
6. Skip CDC-ACM Control Transfers
7. Discover Vendor-Specific Bulk Endpoints
8. Setup USB Transfers on Single Interface
9. Begin UBX Protocol Communication
Current Status: UNSUPPORTED - These interfaces are not functional in the current driver.
Multiple approaches were attempted to enable UART1/UART2 interfaces:
- USB Packet Analysis: Discovered 2-byte status headers per 64-byte USB packet
- Header Stripping: Successfully removed status headers but received all zeros (no UART data)
- USB-to-UART Bridge Initialization: Attempted various vendor control sequences:
- Set baudrate (38400)
- Configure line format (8N1)
- Enable UART mode
- Enable bridge functionality
- CDC Control Transfers: Failed with
LIBUSB_ERROR_PIPE(expected for vendor-specific interfaces)
Despite UBX being enabled by default on UART1, the USB-to-UART bridge initialization sequence is incomplete. The specific vendor control protocol for this chip is unknown.
// Current implementation blocks UART1/UART2 interfaces with clear error message
if (device_family_ == ublox_dgnss::DeviceFamily::X20P &&
(dev_desc.idProduct == 0x050c || dev_desc.idProduct == 0x050d)) {
const char* interface_name = (dev_desc.idProduct == 0x050c) ? "UART1" : "UART2";
std::string error_msg = std::string("X20P ") + interface_name + " interface (0x" +
std::to_string(dev_desc.idProduct) + ") is not currently supported. " +
"Please use the main X20P interface (0x01ab) instead. " +
"See GitHub issue: https://github.com/aussierobots/ublox_dgnss/issues/48";
throw UsbException(error_msg);
}Use the main X20P interface (0x01ab) which provides identical functionality to F9P/F9R devices and supports all GNSS operations.
| Device Family | USB Connection | Parameter Support | Multi-Device | Serial Handling |
|---|---|---|---|---|
| F9P | ✅ Implemented | ✅ Full Support | ✅ Single Instance | ✅ Optional/Search |
| F9R | ✅ Implemented | ✅ Full Support | ✅ Single Instance | ✅ Optional/Search |
| X20P (0x01ab) | ✅ Implemented | ✅ Full Support | ✅ Single Instance | ✅ Factory Display |
| X20P (UART1/2) | ❌ Not Supported | ❌ Blocked | ❌ See Issue #48 | ✅ Factory Display |
- Multi-Family Support: F9P, F9R, and X20P devices supported simultaneously
- Device Family Detection: Automatic USB architecture adaptation
- Parameter Validation: Case-insensitive family specification (x20p/X20P, f9p/F9P)
- USB Architecture Handling: CDC-ACM and Vendor-Specific class support
- Product ID Management: Single and multiple product ID families
- Interface Management: Dynamic interface count validation and claiming
- Endpoint Discovery: Class-aware endpoint identification
- Control Transfer Management: Device family-specific USB setup
- Backward Compatibility: Existing F9P/F9R devices unchanged
- Forward Compatibility: Framework for future device families
- Mixed Environment Support: Multiple device families in single system
- Robust Error Handling: Device family-specific error interpretation
- Extensible Design: Easy addition of new device families
- Search Parameter: User-specified serial for device selection
- Device Serial: Actual iSerial from USB descriptor (when available)
- Display Logic: Currently shows search parameter in logs
- F9P/F9R: iSerial typically empty, search parameter used for identification
- X20P UART (0x050c/0x050d): Factory iSerial always available, should be displayed for device identification
- X20P General (0x01ab): u-center programmed iSerial (e.g., "DBENI0OW", "DBENI0P4"), may be empty if not programmed
# List all u-blox devices (vendor ID 1546)
lsusb -d 1546:
# Get detailed device information including serial numbers
lsusb -d 1546: -v | grep -E "idProduct|iSerial"
# X20P devices - check for factory serial numbers
lsusb -d 1546:050c -v | grep iSerial # UART1 interface
lsusb -d 1546:050d -v | grep iSerial # UART2 interface
lsusb -d 1546:01ab -v | grep iSerial # General interface
# F9P/F9R devices - may show "iSerial: 0" (not programmed)
lsusb -d 1546:01a9 -v | grep iSerial# X20P UART device with factory serial
$ lsusb -d 1546:050c -v | grep iSerial
iSerial 3 [Factory_ID]
# F9P device without factory serial
$ lsusb -d 1546:01a9 -v | grep iSerial
iSerial 0
# X20P General device with user-programmed serial
$ lsusb -d 1546:01ab -v | grep iSerial
iSerial 3 DBENI0OW
# Multiple X20P devices (mixed interfaces)
$ lsusb -d 1546: -v | grep -E "idProduct|iSerial"
idProduct 0x050c
iSerial 3 [Factory_ID1]
idProduct 0x01ab
iSerial 3 DBENI0OW# Monitor hotplug events
udevadm monitor --subsystem-match=usb
# Check kernel driver attachment
lsusb -t | grep -i ublox- NEO-M9N devices (1546:01ab or other IDs)
- ZED-F9K dead reckoning modules
- Future u-blox generations
- Simultaneous dual UART operation for X20P
- Device priority management when multiple devices present
- Automatic failover between UART1/UART2
- USB 3.0 support for higher-bandwidth devices
- Power management integration
- USB suspend/resume handling
- Device-specific parameter sets based on USB characteristics
- Automatic configuration detection from USB descriptors
- Hot-swappable device families in same session
- ✅ Fix interface count validation
- ✅ Dynamic interface claiming
- ✅ Basic device family awareness
- 🔄 USB class-aware endpoint discovery
- 🔄 Improved error messages with device context
- 🔄 Vendor-specific protocol handling
- 📋 Multi-device coordination
- 📋 Enhanced hotplug for dual-device families
- 📋 Configuration optimization per device family
- 📋 Additional device family support
- 📋 Advanced USB features
- 📋 Performance optimizations
Device Family | Interface Count | USB Class | Serial String | Connection Status | UBX Communication
F9P | 2 | CDC-ACM | Optional | ✅ Verified | ✅ Working
F9R | 2 | CDC-ACM | Optional | ✅ Verified | ✅ Working
X20P (0x01ab) | 2 | CDC-ACM | User Set | ✅ Verified | ✅ Working
X20P (UART) | 1 | Vendor | Factory Set | ❌ Unsupported | ❌ Issue #48
- ✅ Single device connection - All families connecting successfully
- ✅ Device family parameter validation - Case-insensitive support (x20p/X20P)
- ✅ USB architecture adaptation - Interface count and class handling
- ✅ Serial string handling - Device family-aware iSerial processing
- ✅ Multi-product ID support - X20P dual UART product ID detection
- ✅ Actual device serial display - X20P UART displays factory iSerial, X20P General/F9P/F9R show search parameter
- X20P General (0x01ab): Successfully tested with user-programmed serials "DBENI0OW", "DBENI0P4"
- X20P UART (0x050c): Factory serial available but specific content varies by device
- F9P/F9R: Previously verified with CDC-ACM architecture
- Parameter System: All device families integrate with ROS2 parameter framework
- USB Initialization: All families complete initialization successfully
- ❌ X20P UART1/UART2 interfaces (0x050c/0x050d): Not supported due to unknown USB-to-UART bridge protocol
- Impact: Users must use main interface (0x01ab)
- Tracking: GitHub Issue #48
- Workaround: Main interface (0x01ab) provides full functionality
- ✅ Primary Interface (0x01ab): Full F9P/F9R compatibility with CDC-ACM protocol
- ❌ UART1 Interface (0x050c): Blocked with clear error message
- ❌ UART2 Interface (0x050d): Blocked with clear error message
- ✅ Serial string display enhancement - X20P now displays actual device iSerial
- X20P UART2 testing - Verify 0x050d product ID handling
- Multi-device scenarios - Multiple X20P devices with serial string selection
- Mixed environment testing - F9P/F9R and X20P devices simultaneously
This document provides comprehensive reference for u-blox device family USB architecture differences and implementation patterns.