So recently, I was tasked with tasked with integrating USB HID-compliant scales into my company's Electron WMS shipping app. I found it pretty difficult to find good information on identifying USB scales, so I'm making this guide in hopes of helping someone else in the same situation.
This document will give examples on identifying and reading from scales in node.js.
This part is actually pretty easy, but it took me a while to figure it out. I found the USB Usage Table spec document which then made it really easy. On page 414 (section 33), you'll find the Scales usage page which is 0x8D (141 in decimal).
The easiest way is to use the node module node-hid
, use npm install node-hid
to add it to your project.
Add it to your desired js file const HID = require('node-hid');
Implement a function similar to this
function getWeightScales() {
const devices = HID.devices();
let scales = [];
devices.forEach((device) => {
if (device.usagePage == 141) {
scales.push(device);
}
});
return scales;
}
This will populate the scales array with all devices in usage Page 141, which should only be USB Scales.
This describes the bytes you'll read from the HID data.
Byte 0 == Report ID
Byte 1 == Scale Status
Byte 2 == Weight Unit
Byte 3 == Data Scaling (decimal placement) - signed byte is power of 10
Byte 4 == Weight LSB
Byte 5 == Weight MSB
In the HID usage doc, each of the above is listed in order to their IDs.
With scale status, for example, if you go to page 421 (section 33.6), fault is 1, stable @ 0 is 2, etc.
First connect to the device:
var device = new HID.HID(devicePath);
Device path can be found if you list out all currently connected devices
const devices = HID.devices();
You can then of course filter out by usage page 141 for a list of scales.
Do something like this to find your device (if you keep track of the vendor and product id somewhere):
devices.forEach((device) => {
if (device.vendorId == this.vendorId && device.productId == this.productId) {
devicePath = device.path;
}
});
Listen for when data is sent from the scale:
device.on('data', (data) => {
// handle data
});
You can read from the scale like this:
const LSB = data[4];
const MSB = data[5];
const combinedWeight = (MSB << 8) | LSB;
const scalingFactor = Math.pow(10, (data[3] << 24) >> 24);
let weight = parseFloat(combinedWeight * scalingFactor).toFixed(2);
Of course account for the weight unit it's being sent in and convert it to the unit you want, something like this:
const weightUnit = data[2];
// This converts from kilos & ounces to pounds.
switch (weightUnit) {
case 3: // Kilos
weight = parseFloat(parseFloat(weight) * 2.20462262185).toFixed(2);
break;
case 11: // Ounces
weight = parseFloat(parseFloat(weight) * 0.0625).toFixed(2);
break;
case 12: // Pounds
default:
break;
}
For other weight units, see the HID document listed above on page 419 (section 33.4.1): 1 is Milligram, 2 is Gram, etc.
I hope this document clears out some confusion on reading usb-hid scales. This document also shows there is no reason to store a huge list of vendor ids to guess at what 'might' be a scale, like what most shipping software seems to do. While the examples here use node-hid
, the knowledge is portable to any programming language. I hope this proves helpful.