@kripton3000
Бекенд- и немного фронтенд-разработчик

Как прочитать данные с USPS Scale(PS-10USB)?

Имею в наличие вот такие весы PS-10USB, под винду была найдено прога на странице, windows я не пользуюсь, но работоспособность её проверил. Под Debian взял скрипт на Python'е, найденный в сети:
#!/usr/bin/env python

"""
ID 04d9:8010 Holtek Semiconductor, Inc.
"""
import usb.core
import usb.util as util
from array import array
import sys
from optparse import OptionParser, make_option
import pickle
from struct import unpack

dev = None

VID = 0x04d9
PID = 0x8010

# define HID constants

REQ_HID_GET_REPORT = 0x01
REQ_HID_GET_IDLE = 0x02
REQ_HID_GET_PROTOCOL = 0x03

REQ_HID_SET_REPORT = 0x09
REQ_HID_SET_IDLE = 0x0A
REQ_HID_SET_PROTOCOL = 0x0B

HID_REPORT_TYPE_INPUT = 1 << 8
HID_REPORT_TYPE_OUTPUT = 2 << 8
HID_REPORT_TYPE_FEATURE = 3 << 8


def openDev():
    global dev
    dev = usb.core.find(idVendor=VID, idProduct=PID)
    if dev is None:
        raise ValueError('Device not found')

    if dev.is_kernel_driver_active(0):
        dev.detach_kernel_driver(0)
    # do reset and set active conf. Must be done before claim.
    dev.set_configuration()
    util.claim_interface(dev, None)


def setReport(reportId, data):
    r = dev.ctrl_transfer(
        util.build_request_type(
            util.CTRL_OUT, util.CTRL_TYPE_CLASS, util.CTRL_RECIPIENT_INTERFACE),  # bmRequestType,
        REQ_HID_SET_REPORT,
        HID_REPORT_TYPE_OUTPUT | reportId,
        0,                 # feature_interface_num
        data,              # data
        3000               # timeout (1000 was too small for C_FREE)
    )
    print >> (HID_REPORT_TYPE_OUTPUT)
    return r


def read():
    openDev()
    try:
        dev.read(0x81, 64)  # flush
    except usb.core.USBError:
        pass

    setReport(9, array('B', (0x10, 0, 0, 0, 0, 0, 0, 0)))

    # Every user can have upto 12 variables
    # and additional 8*64 for user data
    r = []
    for i in xrange(120 + 8):
        x = dev.read(0x81, 64)
        if not x:
            print >> sys.stderr, "Read failed"
            exit(1)
        if x[0] == 0xff and (i < 120):
            print >> sys.stderr, "Invalid data", x
            exit(1)
        r.append(x)

    # Each variable can have upto 32 values
    frmt = "!" + "H" * 32
    s = []
    for i in xrange(120):
        x = unpack(frmt, r[i])

        # Variables 5 .. 10 are not available on my scale
        if i > 5 and i < 10:
            err = [item for item in x if item != 0]
        else:
            err = [item for item in x if item == 0xffff]
        if len(err):
            print >> sys.stderr, "INVALID:", err
            exit(1)

        s.append(x)
    return s


def printUser(s, user):
    # Transpose from 12x32 to 32x12 and format

    j = 12 * user
    for i in xrange(32):
        x = s[j + 5][i]  # date
        if not x:
            break
        print "{:d}-{:02d}-{:02d}T07:00:00 {:.1f} {:.1f} {:.1f} {:.1f} {:.1f} {:d} {:d}".format(
            1920 + (x >> 9), x >> 5 & 0xf, x & 0x1f,
            s[j + 0][i] / 10., s[j + 1][i] / 10., s[j + 2][i] / 10., s[j + 3][i] / 10., s[j + 4][i] / 10., s[j + 10][i], s[j + 11][i])


def main():

    option_list = [
        make_option("-c", "--cached", action="store_true", dest="cached"),
        #make_option("-w", "--write",action="store",      dest="output_file"),
        make_option('-u', '--user', action='store',      dest='user'),
    ]

    parser = OptionParser(
        option_list=option_list, usage='Usage: %prog [options]')
    options, args = parser.parse_args()

    if options.cached:
        with open("dump.pickle") as f:
            s = pickle.load(f)
    else:
        s = read()
        with open("dump.pickle", "w") as f:
            pickle.dump(s, f)

    user = 0
    if options.user:
        user = int(options.user) - 1

    printUser(s, user)


if __name__ == '__main__':
    main()

Но при исполнении получаю следующее:

Traceback (most recent call last):
  File "test.py", line 145, in <module>
    main()
  File "test.py", line 133, in main
    s = read()
  File "test.py", line 70, in read
    setReport(9, array('B', (0x10, 0, 0, 0, 0, 0, 0, 0)))
  File "test.py", line 57, in setReport
    3000               # timeout (1000 was too small for C_FREE)
  File "/usr/local/lib/python2.7/dist-packages/usb/core.py", line 971, in ctrl_transfer
    self.__get_timeout(timeout))
  File "/usr/local/lib/python2.7/dist-packages/usb/backend/libusb1.py", line 819, in ctrl_transfer
    timeout))
  File "/usr/local/lib/python2.7/dist-packages/usb/backend/libusb1.py", line 552, in _check
    raise USBError(_strerror(ret), ret, _libusb_errno[ret])
usb.core.USBError: [Errno 32] Pipe error

Испробовал все версии pyusb (1.0.0a2, 1.0.0a3, 1.0.0b1, 1.0.0b2) которые есть в pip, ставил последнюю с git'a - эффекта ноль, также пробовал разные backend'ы.

lsusb -v -d 04d9:8010
Bus 001 Device 010: ID 04d9:8010 Holtek Semiconductor, Inc. 
Couldn't open device, some information will be missing
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               1.10
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0         8
  idVendor           0x04d9 Holtek Semiconductor, Inc.
  idProduct          0x8010 
  bcdDevice            1.00
  iManufacturer           1 
  iProduct                2 
  iSerial                 0 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           34
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 No Subclass
      bInterfaceProtocol      0 None
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.10
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      53
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval

dmesg
[ 8202.703242] usb 1-1.2: new low-speed USB device number 10 using ehci-pci
[ 8202.813391] usb 1-1.2: New USB device found, idVendor=04d9, idProduct=8010
[ 8202.813397] usb 1-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 8202.813400] usb 1-1.2: Product: USB-HID demo code
[ 8202.813403] usb 1-1.2: Manufacturer: Holtek
[ 8202.830331] hid-generic 0003:04D9:8010.0009: hiddev0,hidraw2: USB HID v1.10 Device [Holtek USB-HID demo code] on usb-0000:00:1a.0-1.2/input0

dpkg -l | grep usb
ii  libgusb2:amd64                                              0.1.6-5                             amd64        GLib wrapper around libusb1
ii  libusb-0.1-4:amd64                                          2:0.1.12-25                         amd64        userspace USB programming library
ii  libusb-1.0-0:amd64                                          2:1.0.19-1                          amd64        userspace USB programming library
ii  libusb-1.0-0-dev:amd64                                      2:1.0.19-1                          amd64        userspace USB programming library development files
ii  libusb-1.0-doc                                              2:1.0.19-1                          all          documentation for userspace USB programming
ii  libusbmuxd2:amd64                                           1.0.9-1                             amd64        USB multiplexor daemon for iPhone and iPod Touch devices - library
ii  libusbredirparser1:amd64                                    0.7-1                               amd64        Parser for the usbredir protocol (runtime)
ii  usb-modeswitch                                              2.2.0+repack0-2                     amd64        mode switching tool for controlling "flip flop" USB devices
ii  usb-modeswitch-data                                         20150115-1                          all          mode switching data for usb-modeswitch
ii  usbutils                                                    1:007-2                             amd64        Linux USB utilities
ii  xserver-xorg-video-sisusb                                   1:0.9.6-2+b2                        amd64        X.Org X server -- SiS USB display driver

pip freeze | grep usb
libusb1==1.3.0
pyusb==1.0.0b2

/etc/hidraw2 и /etc/usb/hiddev0 всегда пустые, в них ничего не пишется.

Помогите прочитать хоть какие-нибудь данные с данных весов.

usbscale тоже пробовал - долго висит и возвращает
Error in USB transfer
  • Вопрос задан
  • 634 просмотра
Решения вопроса 1
@coolynx
Я тоже пользуюсь этим скриптом na CentOS и Raspbian на RaspberryPi. На разных девайсах разные проблемы.
Но, кажется тут проблема в том что нет допуска к данным:
Couldn't open device, some information will be missing
...
Report Descriptors:
** UNAVAILABLE **


У меня вот что выдает lsusb -v -d 04d9:8010
idVendor 0x04d9 Holtek Semiconductor, Inc.
idProduct 0x8010
bcdDevice 1.00
iManufacturer 0
iProduct 2 Scale
...
Report Descriptor: (length is 53)
Item(Global): Usage Page, data= [ 0x00 0xff ] 65280
(null)
Item(Local ): Usage, data= [ 0x01 ] 1
(null)
Item(Main ): Collection, data= [ 0x01 ] 1
Application


На Python 2.7.x нужно поменять.

util.claim_interface(dev, None)

на
util.claim_interface(dev, 0)

На маке к сожалению не будет работать.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@monah_tuk
Причём тут C++? А вообще, libusb любит возвращать PIPE ERROR когда устройство делает Stall на запрос, что говорит об ошибке его обработки или что реквест не поддерживается. Косвенно подтверждается этим:
Report Descriptors: 
           ** UNAVAILABLE **

а при этом делается setReport

Далее, судя по дескрипторам это обычное HID устройство. Попробуйте до него достучаться при помощи: www.signal11.us/oss/hidapi/:
sudo apt-get install libhidapi-dev

usbscale тоже пробовал - долго висит и возвращает

глупый вопрос, но вы свои VID:PID сюда добавляли:
https://github.com/erjiang/usbscale/blob/master/sc...
?

Ещё, тут: https://github.com/erjiang/usbscale/blob/master/us... в этом месте:
// 
        // If the data transfer succeeded, then we pass along the data we
        // received tot **print_scale_data**.
        //
        if(r == 0) {

они проверяют код ошибки на 0, это правильно, но какой именно ошибкой завершается запрос - непонятно. Судя по тому, что вы говорите ("долго висит"), у них там используется большой таймаут (10 сек), то код ошибки - LIBUSB_ERROR_TIMEOUT, а в документации сказано:

Also check transferred when dealing with a timeout error code. libusb may have to split your transfer into a number of chunks to satisfy underlying O/S requirements, meaning that the timeout may expire after the first few chunks have completed. libusb is careful not to lose any data that may have been transferred; do not assume that timeout conditions indicate a complete lack of I/O.


они ожидают блок размером WEIGH_REPORT_SIZE (6 байт), вдруг так случается, что размер у ваших весов поменьше? 5 байт? Т.е. нужно, для начала, подправить программку и залогировать код ошибки и значение переменной len. Если это действительно таймаут, а не PIPE ERROR, то уже проверять значение len на выходе, если оно больше нуля - пробовать процессить эти данные. Только процессинг, скорее всего, должен быть уже другой какой.

Основное же отличие от питоновской программы: не далется setReport. Как следствие, можете попробовать закомментировать эту строчку в питоновском скрипте, как быструю пробу.

Финальным же аккордом может стать реверсинг общения весов и виндовой программы при помощи Wireshark :-)

Если не сможете сами, можете сделать мне заказ, заинтересует - пишите в почту (в профиле).
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы