0

The good news is this cheap Xiamen ELANE.NET load cell powers-up on USB into Report 3 mode; barfing its current weight in grams, constantly.

Here's its datasheet:

http://www.elane.net/USBscales/List_USB_Digital_Load_Cell_Commands_and_Data_Format_User.pdf

I can read that with standard pyusb calls. This sample could read the scale...

http://www.orangecoat.com/how-to/read-and-decode-data-from-your-mouse-using-this-pyusb-hack

... if you replace the device lookup with usb.core.find(idVendor=0x7b7c, idProduct=0x301)

(I also abuse sudo to run my program, bc I decline to muck around with the device permissions, and sudo is easy on a Raspberry Pi.)

Using standard pyusb calls, I can read my scale's spew like this:

device.read(endpoint.bEndpointAddress, endpoint.wMaxPacketSize)

That returns a 6 byte array:

 +--------------------- Report type
 |  +------------------ Weight Stable (tray not moving)
 |  |  +--------------- grams (not pounds)
 |  |  |  +------------ whatever
 |  |  |  |  +--------- 2 grams
 |  |  |  |  |  +------ 0 x 256 grams
 |  |  |  |  |  |
 V  V  V  V  V  V
[3, 4, 2, 0, 2, 0]

Now the fun starts when I try to send commands to the scale. The command to zero-out the current weight (Zero Weight, aka "tare") might be 7 4 2 0 0 0.

If I use sample code like https://github.com/walac/pyusb/blob/master/docs/tutorial.rst to find the ENDPOINT_OUT endpoint, and write to it using either of these lines, I can't tare:

# ep_out.write('\x07\x04\x02\x00\x00\x00', 6)
ep_out.write([0x07, 0x04, 0x02, 0x00, 0x00, 0x00], 6)

(The symptom is, I can put a load on my load cell, weigh it with the above .read() line, then tare, then not get a zero when I .read() again.)

Okay, we're not dead yet. We haven't tried any HIDAPI. So I apt-get me some libusbhid-common, some cython-dev, some libusb-dev, some libusb-1.0.0-dev, and some libudev-dev, and I upgrade the HIDAPI C example code to attempt a tare:

handle = hid_open(0x7b7c, 0x301, NULL);
buf[0] = 0x07; 
buf[1] = 0x04;
buf[2] = 0x02;
res = hid_write(handle, buf, 3);

And that tares.

To replicate my one success in Python (despite how tempting rewriting one small layer of my app in C++ is!), I whip out some Cython-hidapi (presumably from git://github.com/signal11/hidapi.git), and upgrade their try.py example code:

h = hid.device()
h.open(0x7b7c, 0x301)

print("Manufacturer: %s" % h.get_manufacturer_string())
print("Product: %s" % h.get_product_string())
print("Serial No: %s" % h.get_serial_number_string())

res = h.write([0x07, 0x04, 0x02, 0,0,0])

Guess what? The last line does not tare. But it DOES tare if I run it 3 times!

res = h.write([0x07, 0x04, 0x02, 0,0,0])
res = h.write([0x07, 0x04, 0x02, 0,0,0])
res = h.write([0x07, 0x04, 0x02, 0,0,0])

So, before I write a loop that calls the tare line over and over until the read returns a level zero, could someone check my math and suggest a shortcut? A raw pyusb solution would work great, too.

Phlip
  • 5,253
  • 5
  • 32
  • 48

1 Answers1

1

I did a few little HID programs in Python over the last weeks, with just pyusb and they seem to work very reliably.

Did you check if the write command you issue prompts a reply? In that case you have to read that. This is the initialization code:

def claimed(self):
    self.hdev = ucore.find(idVendor = VID, idProduct = PID)

    #pdb.set_trace()
    if self.hdev.is_kernel_driver_active(0):
        print "Kernel driver is active."
        self.hdev.detach_kernel_driver(0)
        print self.hdev

    self.hdev.set_configuration()
    print "config set"
    self.cfg = self.hdev.get_active_configuration()

    return True

After that, it's just

self.hdev.write(endpoint.bEndpointAddress, self.data)

and

self.data = self.hdev.read(endpoint.bEndpointAddress, 64, 64)

as necessary. The selfs are there because the function and the statements are part of a class which handles the peripheral, and share the hdev variable.

EDIT: The PDF you refer to downloads just one page, and the command 7,4,2,0,0,0 is not documented in there. Is there more complete information available?

Also, I found a few indication which might be of use to you. According to this article, there is no need to interrogate the scales continuously:

http://www.elane.net/UserManuals/USB%20Interface%20Protocol%20%285-kg%20Model%29.pdf

And according to the following article, it seems necessary to interrogate several times (up to 10), which I suspect may have something to do with the conversion time of the AD. The article is about a Dymo scale, but the protocol seems somewhat similar:

http://steventsnyder.com/reading-a-dymo-usb-scale-using-python/

jcoppens
  • 5,306
  • 6
  • 27
  • 47
  • While you are correct that neither I nor the "Read... your Mouse" page I linked called `set_configuration()`, adding your `claimed()` routine did not fix the bug. I up-voted it to tell the community it's confirmed not to make things worse. I also added a call to `usb.util.find_descriptor()`, per http://sourceforge.net/p/pyusb/mailman/message/26198542/ , to dig up the output endpoint, and write to that. The bug remains I must write 3 times for the tare to take. I suppose this is acceptable losses, and it's good to know I can bypass the HID stack for this light stuff. – Phlip Apr 26 '15 at 01:33
  • Philip, I added an edit to the reply, with a few more suggestions. – jcoppens Apr 26 '15 at 14:11
  • I have a hard-copy of a Xiamen PDF, and I e-searched, for about an hour, clicking on every PDF on elane.net, and couldn't find it. About the "protocol seems somewhat similar", yes I think everyone just packages the same chipset (and assumes someone else documented it!) – Phlip Apr 26 '15 at 14:25
  • I will report back here if I successfully change the readout from grams to pounds. (Except then I'm worried the low byte will be ounces!!) About the time the command takes to sink in; when I tare 3x, I can see a few "leftover" readings with the wrong tare, before the right ones appear. When I tare only once, the wrong tare almost always continues. – Phlip Apr 26 '15 at 14:25