Files
dymo-twin-turbo/README.md
2025-08-08 21:53:16 -05:00

81 lines
5.0 KiB
Markdown

It appears the Dymo 400 Twin Turbo does not report the correct device serial number. This prevents us from using more than one of these devices on a system because the device URI is not properly formed.
This will possibly need a USB device quirk added to the Linux kernel.
An alternative is to possibly implement a fix in dymo-cups-drivers. However, I think the problem occurs higher up the software stack.
How to identify the problem
1. Load wireshark and start recording packets from the master USB bus (bus:0) just prior to connecting the printer
2. Connect a Dymo 400 Twin Turbo printer to the system with USB
3. Stop logging USB packets after a minute
4. Review packet data and notice a packet received from the device has a malformed string
- The packet will be a GET DESCRIPTOR Response STRING with bLength: 30, bDescriptorType: 0x03 (String), and malformed bString: `〰㘀㄀ ㌀ ㄀ ㄀㘀㄀㔀㜀㔀`
5. In terminal, `lsusb -v -d 0922:0018`
6. Notice the gibberish value in the iSerial field.
7. When setting up the printer, notice the device URI is `usb://DYMO/LabelWriter%20Twin%20Turbo?serial=??????????????`
The problem occurs because the default format for USB device descriptor STRINGs is uint-16-le. However, the Dymo-TT includes an extra byte at the start of the data returned from the device.
For example, the serial bString from a Dymo 450 is received as `b'\x31\x00\x37\x00\x31\x00\x30\x00\x30\x00\x36\x00\x31\x00\x36\x00\x31\x00\x38\x00\x34\x00\x32\x00\x33\x00\x30\x00'`.
However, an example from the Dymo-TT is `b'\x30\x30\x00\x36\x00\x31\x00\x30\x00\x33\x00\x30\x00\x31\x00\x30\x00\x31\x00\x36\x00\x31\x00\x35\x00\x37\x00\x35'`.
Decoding the Dymo 450 string using uint-16-le yields "17100616184230", whereas the Dymo-TT string yields gibberish values.
The physical labels found on the Dymo 450 include "1740021750110" and another "1750110". The physical labels on the Dymo-TT are "93085-2053234" and "06440293085". It seems the digital "serial" numbers don't match the physical serial numbers marked. However, the digital serial ID is still unique for each device.
A second Dymo-TT connected previously reports `〰㘀 㠀\xe3\x84\x80㈀\xe3\x84\x80㐀㐀㔀㔀㌀㔀㤀` as the device SerialNumber as recorded in the kernel log. It's unknown what the actual binary value should be from this.
```python
# from pyUSB
import usb.core
import usb.util
vid = 0x0922 # Dymo VID
pid450 = 0x0020 # Dymo 450 PID
pidtt = 0x0018 # Dymo Twin Turbo PID
dymo450 = usb.core.find(idVendor=vid, idProduct=pid450)
dymott = usb.core.find(idVendor=vid, idProduct=pidtt)
print(f"Dymo 450 Serial: {dymo450.serial_number}") # yields 17100616184230
print(f"Dymo-Twin-Turbo Serial: {dymott.serial_number}") # yields gibberish
print(f"Dymo 450 Raw Serial value: {usb.control.get_descriptor(dymo450,254,0x03,dymo450.iSerialNumber,dymo450.langids[0])[2:30].tobytes().decode('utf-16-le')}") # returns 17100616184230
print(f"Dymo-TT Raw Serial value: {usb.control.get_descriptor(dymott,254,0x03,dymott.iSerialNumber,dymott.langids[0])[2:30].tobytes().decode('utf-16-le')}") # returns gibberish
print(f"Dymo 450 string result as bytes: {usb.control.get_descriptor(dymo450,254,0x03,dymo450.iSerialNumber,dymo450.langids[0])[2:30].tobytes().decode('utf-16-le').encode('utf-8')}") # returns b'17100616184230'
print(f"Dymo 450 string result as bytes: {usb.control.get_descriptor(dymott,254,0x03,dymott.iSerialNumber,dymott.langids[0])[2:30].tobytes().decode('utf-16-le').encode('utf-8')}") # returns b'\xe3\x80\xb0\xe3\x98\x80\xe3\x84\x80\xe3\x80\x80\xe3\x8c\x80\xe3\x80\x80\xe3\x84\x80\xe3\x80\x80\xe3\x84\x80\xe3\x98\x80\xe3\x84\x80\xe3\x94\x80\xe3\x9c\x80\xe3\x94\x80'
```
The Twin-Turbo serial is supposed to be 006103010161575. Due to the extra 0 byte `b'\x30'`, the rest of the return data becomes shifted and is interpreted like
`b'\x30\x30\x00\x36\x00\x31\x00\x30\x00\x33\x00\x30\x00\x31\x00\x30\x00\x31\x00\x36\x00\x31\x00\x35\x00\x37\x00\x35'.decode('utf-16-le')` or literally unicode values `\x3500\x3700\x3500\x3100\x3600\x3100\x3000\x3100\x3000\x3300\x3000\x3100\x3600\x3030`
Dymo 450 GET_DEVICE_ID example string:
```
'MFG:DYMO;CMD: ;MDL:LabelWriter 450;CLASS:PRINTER;DESCRIPTION:DYMO LabelWriter 450;SERN:01010112345600;'
```
Dymo Twin Turbo GET_DEVICE_ID example string (note the null byte before ERN:):
```
'MFG:DYMO;CMD: ;MDL:LabelWriter Twin Turbo;CLASS:PRINTER;DESCRIPTION:DYMO LabelWriter Twin Turbo;\x00ERN:006103010161575'
```
Note: Linux Mint 22.1 <- Ubuntu 24.04 Noble <- Debian Trixie/sid <- Kernel 6.8.0-71-generic; pyusb version 1.3.1; python version 3.12.3
References
https://www.beyondlogic.org/usbnutshell/usb5.shtml#StringDescriptors
https://github.com/pyusb/pyusb/issues/154
https://github.com/pyusb/pyusb/blob/master/usb/util.py#L320
https://github.com/torvalds/linux/blob/master/drivers/usb/core/quirks.c
https://docs.kernel.org/admin-guide/reporting-issues.html
https://kernel.org/category/faq.html
https://bugzilla.kernel.org/show_bug.cgi?id=220422
https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/LegacyCollaterals/01233a.pdf