Reading a Stamps.com USB Scale from C#
I’ve managed to use Stamps.com’s PDK to integrate Skiviez’s USPS shipping down to a pretty automatic process. First, you dump a package onto an old serial scale. Second, you scan the order number barcode. Our software (which is constantly polling that scale) reads the weight, loads the order, and passes this information off to the Stamps.com COM object (try saying “Stamps.com COM object” without sounding like an idiot; this is why I hate companies with “.com” in their name). Then, third, you usually just have to smash the Enter key and a delivery label comes shooting out of the printer. Great.
But we haven’t been using the little USB scale that was supplied with our Stamps.com account; instead, the software has been reading from an old serial scale that we got from UPS. Why? I knew how to read data from the serial scale (which is simple enough: write a newline to the serial port and get an ASCII string with the weight back), but the Stamps.com USB scale was a black box. First, I had no idea how even to begin to access data from a USB device in C#. Second, I wouldn’t know how to read data coming back from the scale even if I did.
Here’s my story of how I figured this out. I’m sure that there are more intelligent ways that I could have done this; if there are, I’d love to hear from you.
First, I sat down at my computer with Device Manager open and plugged in the scale. I notice that it appeared in Windows as a “USB Human Interface Device.” Now that’s interesting, I thought to myself. In all my years of futzing around with Windows, I see things pop up as a USB Human Interface Device all the time. I wonder what in the blue hell that actually means?
A quick trip to Google reveals that devices that are HIDs can send and receive data according to a common specification that is detailed in a several hundred page tome that I would care not to read. That means that the operating system can provide one generic HID driver that all of these devices can share. That’s a relief because it means that I don’t need to learn how to interact with some low-level, custom driver that was written specifically for this USB scale.
Since it appears that there is no native .NET way to interact with these HID devices, however, I began searching for a pre-built library. I struck gold with Mike O’Brien’s USB HID library, which has an easy-to-use API that enabled me to complete this project without having any clear idea of what exactly I was doing. These HID devices can send “reports” according to some common protocol; this library lets me read the reports, but figuring out the content of the report is still up to me to figure out.
So, having the scale hooked up, and using the example code on Mike O’Brien’s page, I hammered out some code that looked something like the following (I looked up the product and vendor ID by using the Properties pane in Device Manager):
HidDeviceData inData; HidDevice[] hidDeviceList; HidDevice scale; hidDeviceList = HidDevices.Enumerate(0x1446, 0x6A73); if (hidDeviceList.Length > 0) { int waitTries; scale = hidDeviceList[0]; waitTries = 0; scale.Open(); if (scale.IsConnected) { inData = scale.Read(250); for (int i = 0; i < inData.Data.Length; ++i) { Console.WriteLine("Byte {0}: {1:X}", i, inData.Data[i]); } } scale.Close(); scale.Dispose(); }
At this point, I was fairly flabbergasted because I actually got a reasonably small amount of data back:
Byte 0: 0x3 Byte 1: 0x4 Byte 2: 0xB Byte 3: 0x0 Byte 4: 0x38 Byte 5: 0x0
Unfortunately, I had no idea what this data meant. So I stared at the numbers. During this test, I had been weighing my iPhone, and the LCD display on the scale was displaying 5.6 ounces.
Hmmm, I thought. I’m pretty much a moron when it comes to hexadecimal, so let me convert these numbers to decimal and see if they make any more sense to me. Here goes:
Byte 0: 3 Byte 1: 4 Byte 2: 11 Byte 3: 0 Byte 4: 56 Byte 5: 0
Well, that didn’t help a whole–wait a minute! Byte 4 says “56″ and there are 5.6 ounces on the scale? Coincidence? I think not!
So I added a Sharpie on top of my iPhone and ran the application again. 7.8 ounces and byte 4 says 78.
Like any good engineer, I think to myself, if it happens three times, it must be true! Sure enough, replacing the items with a dead hard drive that was laying on my desk resulted in 12.3 ounces == 123 in byte 4.
Great. So byte 4 is returning the weight in tenths of an ounce. So all I have to do is take byte 4, move the decimal point once to the left, and there I have it. But what happens when byte 4 overflows? To discover this, I pile up everything onto the scale–iPhone, Sharpie, and two dead hard drives–and the scale displays 2 lb 13.5 oz, or 37.5 oz. The data returned is
Byte 0: 3 Byte 1: 4 Byte 2: 11 Byte 3: 0 Byte 4: 119 Byte 5: 1
This doesn’t seem to make a lick of sense, until I noticed that byte 5 is now displaying a 1 where it always had returned a 0 before. I take a wild guess: if byte 4 is returned tenths of an ounce, could byte 5 be an overflow bit? 1 * 256 = 256 + 119 = 375 … yes! 37.5 ounces!
What happens when byte 5 overflows? Who knows–the scale is only rated for 25 lb, and overflowing bytes 4 and 5 would be like, what, 4,112 lb? Let’s not find out.
After some more twiddling, I discovered that byte 1 returns “4″ when the scale is stable (e.g., not bouncing up and down, trying to figure out the weight) and “not 4″ when it is sliding. Therefore, my code to read from a Stamps.com Model 2500i scale using Mike O’Brien’s library is as follows:
private void GetStampsComModel2500iScaleWeight(out decimal? ounces, out bool? isStable) { HidDeviceData inData; HidDevice[] hidDeviceList; HidDevice scale; isStable = null; ounces = null; hidDeviceList = HidDevices.Enumerate(0x1446, 0x6A73); if (hidDeviceList.Length > 0) { int waitTries; scale = hidDeviceList[0]; waitTries = 0; scale.Open(); // For some reason, the scale isn't always immediately available // after calling Open(). Let's wait for a few milliseconds before // giving up. while (!scale.IsConnected && waitTries < 10) { Thread.Sleep(50); waitTries++; } if (scale.IsConnected) { inData = scale.Read(250); ounces = (Convert.ToDecimal(inData.Data[4]) + Convert.ToDecimal(inData.Data[5]) * 256) / 10; isStable = inData.Data[1] == 0x4; } scale.Close(); scale.Dispose(); } }
I still have no idea what bytes 0, 2, or 3 signify, but I’ve followed the first rule of coding: when it’s working, stop coding.
I hope this helps somebody take a USB scale that Simply Does Not Work and turn it into one that Does. Good luck!







I’be been having problems getting my stemps.com com object to even register in my visual studio 2005. I can’t find it under com and when I Import into studio 2005 smui.dll the classes do not register.
Any suggestions?
Thank you in advance.
Tim.
On the PDK page with the weird URL where you download the documentation and such, there should be a link to a type library file called smui.tlb. Download that file and put it somewhere where it’s not going to move (I dumped mine in Stamps’ folder under Program Files). Then fire up the Visual Studio 2005 Command Prompt, cd to that directory, and type “tlbimp smui.tlb /out:Interop.Smui.dll”. This will create an interop DLL that you can now reference in your project. Next run “regtlibv12 smui.tlb”.
Under Visual Studio’s Add Reference dialog, browse to the Interop.Smui.dll file and it as a reference to your project. If you can run a basic program like the following without crashing, you’re golden:
(I’ve had to do this regtlib dance on any machine I deploy the software integration to as well.) Good luck!
Good find! I’m going to try to parse the data I get from my APC UPS…
HI. I am an IT person, not a developer. I have been scouring the web all night trying to find a driver for my Stamps.com 5lb scale. It looks like you hit the nail on the head. I have VS Express 2008 but I have no idea how to use it. Can you compile an EXE of your app for me?
Much obliged.
Hmm. I’m almost certain that the 5 lb scale would have a different hardware identifier and product identifier that we’d need to discover. Then, we’d also have to assume that the 25 lb (which is what I have) speaks the same format as the 5 lb scale. If you could tell me those identifiers, we could give it a shot.
(To lookup the VID and PID for the device, just go into Device Manager, find the scale in the list [it will probably show up as a USB Human Interface Device ... easiest way is to plug and unplug a few times and see what appears and disappears], bring up its Properties, go to Details, and copy the Device Instance Id. (The important parts are the sections VID and PID, which are separated by ampersands.)
Byte 0 = Report ID (3 is the standard for scale data
input reports)
Byte 1 = Scale Status
1 = Fault
2 = Stable at Zero
3 = In Motion
4 = Stable
5 = Under Zero
6 = Over Weight
7 = Requires Calibration
8 = Requires Re-zeroing
Byte 2 = Weight Unit
…
11 = ounces
12 = pounds
Byte 3 = Data Scaling (This is the exponent that you
raise 10 by to get the decimal
placement)
Byte 4 = Weight LSB
Byte 5 = Weight MSB
Go here: http://www.usb.org/developers/devclass_docs/pos1_02.pdf
Sweet! Thanks for posting this. I suppose 90% of the battle is knowing where to look for the documentation!
This page was the first thing that popped up in google and really gave me a good starting point so if anyone else stumbles upon this like I did, here’s some more info.
I did this without Mike O’Brien’s USB HID library just by using sequential calls to SetupDiGetClassDevs, SetupDiEnumDeviceInfo and SetupDiGetDeviceInterfaceDetail in c# as outlined here : http://www.vsj.co.uk/articles/display.asp?id=600 . From there you can open the scale as a file and read bytes from it. You get the same output as above, just in a round about kind of way.
examples:
(kind of hard to follow but has the overall “structure” for finding a specific usb device)
http://sushantkumar.wordpress.com/tech/c-code-4-usb/
(pretty easy to follow)
http://www.codeproject.com/KB/cs/USB_HID.aspx?fid=398968&df=90&mpp=25&noise=3&sort=Position&view=Quick&fr=101&select=3123155
(some useful nuggets)
http://www.codingforums.com/archive/index.php/t-127218.html
(Page all about HID)
http://www.lvr.com/hidpage.htm
Nice tip. I think Mike’s USB library is using SetupDi* underneath the hood. I’ve dealt with that API before in another project (reading from USB barcode scan guns), and boy, isn’t SetupDi* just the most obtuse interface ever!
Okay, this is gonna sound stupid…but I’m just not quite tech savy. In fact, I muddle my way through a lot of things, and somehow get what I want in the end. (That’s what counts, right?!?)
Okay, so I have a 2550i.
I see your code, above. It’s all greek to me…I’m a cut and paste kinda guy.
Where & how do I paste it…Start/Run, or???
Help!
I just downloaded the USPS Shipping Assistant, and now I’d really like to integrate the scale. (Been using it manually for about a year).
Thanks Nicholas…
@Eric
The code is meant to be used by a software developer, who would embed it in a larger application’s code base. I could shove it into a simple executable, but it probably wouldn’t do what you want: it’d pop up a dialog with the current weight, but it wouldn’t be able to integrate with the USPS Shipping Assistant, since that would be code out of its control, and I doubt that that software has any sort of plug-in mechanism that allows new code to “hook into” the USPS software. I suppose it’s always possible that the latest update to USPS Shipping Assistant may have added native support for that scale, but I wouldn’t bet on it.
Of course, it probably would be possible to do some incredibly hacky solution of using the Windows Accessibility API to have this external code type the weight for you into the USPS Shipping Assistant, but that would probably break with each new update and version of the assistant that gets released.
My recommendations would be to use a USPS device that is supported, to actually use the Stamps.com service, since they obviously know how to read their own scale, or to continue fat-fingering the weight entry. Thanks to closed source software, that’s sometimes just how the cookie crumbles =)
Appreciate the response Nicholas!
Guess you just can’t have everything you want…no matter how small it is, huh?
Try this…
http://members.driverguide.com/driver/detail.php?driverid=1599140