Search code examples
javalibusbdymo

I need to Read data from USB scale, DYMO s100 scale. which Java package is going to work the best?


I have tried using LibUsb to connect I just really need to be able to parse the data from this scale, in the end it is going to be going into Filemaker pro. the issue I have with this current code:

package Model;

import javax.management.Descriptor;
import javax.usb.UsbDevice;
import javax.usb.UsbDeviceDescriptor;
import javax.usb.UsbHub;

import org.usb4java.*;

import java.nio.ByteBuffer;
import java.util.List;

public class main {

    //Vendor ID = 0x0922
    //Product ID = 0x8009
    private static short Vendor_ID = 0x0922;
    private static short Product_ID = (short) 0x8009;

    public static void main(String[] args) {

        Device Dymo_Scale = listDevices(Vendor_ID, Product_ID);

        DeviceHandle handle = new DeviceHandle();
        int result = LibUsb.open(Dymo_Scale, handle);
        if (result != LibUsb.SUCCESS) throw new LibUsbException("Unable to open USB device", result);
        try
        {
            result = LibUsb.setConfiguration(handle, 0);
            if (result != LibUsb.SUCCESS) throw new LibUsbException("Unable to set Configuration", result);
            // Use device handle here
            result = LibUsb.claimInterface(handle, 0);
            if (result != LibUsb.SUCCESS) throw new LibUsbException("Unable to claim interface", result);
            try
            {
                ByteBuffer buffer = ByteBuffer.allocateDirect(8);
                buffer.put(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 });
                int transfered = LibUsb.controlTransfer(handle,
                        (byte) (LibUsb.REQUEST_TYPE_CLASS | LibUsb.RECIPIENT_INTERFACE),
                        (byte) 0, (short) 0, (short) 0, buffer, 5);
                if (transfered < 0) throw new LibUsbException("Control transfer failed", transfered);
                System.out.println(transfered + " bytes sent");
            }
            finally
            {
                result = LibUsb.releaseInterface(handle, 0);
                if (result != LibUsb.SUCCESS) throw new LibUsbException("Unable to release interface", result);
            }
        }
        finally
        {
            LibUsb.close(handle);
        }


    }





    private static Device listDevices(short vendorId, short productId) {
        // Create the libusb context
        Context context = new Context();

        // Initialize the libusb context
        int result = LibUsb.init(context);
        if (result < 0)
        {
            throw new LibUsbException("Unable to initialize libusb", result);
        }

        // Read the USB device list
        DeviceList list = new DeviceList();
        result = LibUsb.getDeviceList(context, list);
        if (result < 0)
        {
            throw new LibUsbException("Unable to get device list", result);
        }

        try
        {
            // Iterate over all devices and list them
            for (Device device: list)
            {
                int address = LibUsb.getDeviceAddress(device);
                int busNumber = LibUsb.getBusNumber(device);
                DeviceDescriptor descriptor = new DeviceDescriptor();
                result = LibUsb.getDeviceDescriptor(device, descriptor);
                if (result < 0)
                {
                    throw new LibUsbException(
                            "Unable to read device descriptor", result);
                }
                if (descriptor.idVendor() == vendorId && descriptor.idProduct() == productId) return device;

            }
        }
        finally
        {
            // Ensure the allocated device list is freed
            LibUsb.freeDeviceList(list, true);
        }

        // Deinitialize the libusb context
        LibUsb.exit(context);
        return null;
    }

}

is I get a USB error:

Exception in thread "main" org.usb4java.LibUsbException: USB error 9: Control transfer failed: Pipe error
    at Model.main.main(main.java:41)

Process finished with exit code 1

I have quite a bit of experience with java itself in networking and other areas. I think my issue might be somewhere in the ByteBuffer area... I am not exactly sure about endpoints or buffers I am also trying to access the device through interface 0 which it seems to make the connection and claim the Interface just fine but I need now to get a reading back from the scale...

Is there a better Package out there that can do what I am looking for? or is there just something I am not doing properly in the Synchronous data transfer. Currently am writing this on a Mac if that makes any bit of difference.

As always, appreciate the help if any can provide.

  • Side note. This is a decently old package and has not been updated in years. Wonder if there is an updated more recent option.

Solution

  • Figured out the issue. I was sending 8 bytes.. scale looks for 6, although dymo does not say this anywhere. their manual is a joke... and they have no documentation. Main.java:

    package Model;
    
    import javax.management.Descriptor;
    import javax.usb.*;
    import javax.usb.event.UsbPipeListener;
    
    import org.usb4java.*;
    
    import java.nio.ByteBuffer;
    import java.util.List;
    
    public class main{
    
        //Vendor ID = 0x0922
        //Product ID = 0x8009
        private static short Vendor_ID = 0x0922;
        private static short Product_ID = (short) 0x8009;
    
        public static void main(String[] args) throws UsbException {
            UsbScaleInterface scale = UsbScaleInterface.findScale();
            scale.open();
            try {
                for (int i = 0; i < 60; i++) {
                    scale.syncSubmit();
                }
            } finally {
                scale.close();
            }
        }
    
    }
    

    UsbScaleInterface.java:

    package Model;
    
    import javax.usb.*;
    import javax.usb.event.UsbPipeDataEvent;
    import javax.usb.event.UsbPipeErrorEvent;
    import javax.usb.event.UsbPipeListener;
    import java.util.List;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    public class UsbScaleInterface  implements UsbPipeListener {
    
        private final UsbDevice device;
        private UsbInterface iface;
        private UsbPipe pipe;
        private byte[] data = new byte[6];
    
        private UsbScaleInterface(UsbDevice device) {
            this.device = device;
        }
    
    
        public static UsbScaleInterface findScale() throws UsbException {
            UsbServices services = UsbHostManager.getUsbServices();
            UsbHub rootHub = services.getRootUsbHub();
            // Dymo M10 Scale:
            UsbDevice device = findDevice(rootHub, (short) 0x0922, (short) 0x8003);
            // Dymo M25 Scale:
            if (device == null) {
                device = findDevice(rootHub, (short) 0x0922, (short) 0x8004);
            }
            // Dymo S100 Scale:
            if (device == null) {
                device = findDevice(rootHub, (short) 0x0922, (short) 0x8009);
            }
            if (device == null) {
                return null;
            }
            return new UsbScaleInterface(device);
        }
    
        private static UsbDevice findDevice(UsbHub hub, short vendorId, short productId) {
            for (UsbDevice device : (List<UsbDevice>) hub.getAttachedUsbDevices()) {
                UsbDeviceDescriptor desc = device.getUsbDeviceDescriptor();
                if (desc.idVendor() == vendorId && desc.idProduct() == productId) {
                    return device;
                }
                if (device.isUsbHub()) {
                    device = findDevice((UsbHub) device, vendorId, productId);
                    if (device != null) {
                        return device;
                    }
                }
            }
            return null;
        }
    
        public void open() throws UsbException {
            UsbConfiguration configuration = device.getActiveUsbConfiguration();
            iface = configuration.getUsbInterface((byte) 0);
            // this allows us to steal the lock from the kernel
            iface.claim(usbInterface -> true);
            final List<UsbEndpoint> endpoints = iface.getUsbEndpoints();
            pipe = endpoints.get(0).getUsbPipe(); // there is only 1 endpoint
            pipe.addUsbPipeListener(this);
            pipe.open();
        }
    
        public void syncSubmit() throws UsbException {
            pipe.syncSubmit(data);
        }
    
    
        public void close() throws UsbException {
            pipe.close();
            iface.release();
        }
    
        @Override
        public void dataEventOccurred(UsbPipeDataEvent upde) {
            boolean empty = data[1] == 2;
            boolean overweight = data[1] == 6;
            boolean negative = data[1] == 5;
            boolean grams = data[2] == 2;
            int scalingFactor = data[3];
            int weight = (data[4] & 0xFF) + (data[5] << 8);
            if (empty) {
                System.out.println("EMPTY");
            } else if (overweight) {
                System.out.println("OVERWEIGHT");
            } else if (negative) {
                System.out.println("NEGATIVE");
            } else { // Use String.format since printf causes problems on remote exec
                System.out.println(String.format("Weight = %,.1f%s",
                        scaleWeight(weight, scalingFactor),
                        grams ? "g" : "oz"));
            }
        }
    
        private double scaleWeight(int weight, int scalingFactor) {
            return weight * Math.pow(10, scalingFactor);
        }
    
    
        @Override
        public void errorEventOccurred(UsbPipeErrorEvent usbPipeErrorEvent) {
            Logger.getLogger(UsbScaleInterface.class.getName()).log(Level.SEVERE, "Scale Error", usbPipeErrorEvent);
        }
    }
    

    Just need to figure out how the tare function might work now and possible way to turn the scale on from the computer. however I don't think this will be an easy task as it would be pretty brute force... Mind you both of these classes are in the same package.