mirror of
https://github.com/erjiang/usbscale.git
synced 2025-07-03 03:14:26 -06:00
Wrote 2/3 of the documentation, docco style
This commit is contained in:
parent
b9fa538ef9
commit
81355c8c18
155
usbscale.c
155
usbscale.c
@ -1,12 +1,35 @@
|
|||||||
|
// usbscale
|
||||||
|
// =========
|
||||||
|
//
|
||||||
|
// C utility to read weight from a USB scale.
|
||||||
|
//
|
||||||
|
// Usage: usbscale
|
||||||
|
//
|
||||||
|
// There are no options. **usbscale** will try to read data from the first
|
||||||
|
// scale it finds when enumerating your USB devices.
|
||||||
|
//
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// This program uses libusb-1.0 (not the older libusb-0.1) for USB
|
||||||
|
// functionality.
|
||||||
|
//
|
||||||
#include <libusb-1.0/libusb.h>
|
#include <libusb-1.0/libusb.h>
|
||||||
|
//
|
||||||
|
// To enable a bunch of extra debugging data, simply define `#define DEBUG`
|
||||||
|
// here and recompile.
|
||||||
|
//
|
||||||
|
// #define DEBUG
|
||||||
|
//
|
||||||
|
// The "scales.h" file contains a listing of all the Vendor and Product codes
|
||||||
|
// that this program will try to read. To try your scale, add your scale's
|
||||||
|
// vendor and product codes to scales.h and recompile.
|
||||||
|
//
|
||||||
#include "scales.h"
|
#include "scales.h"
|
||||||
|
|
||||||
//#define DEBUG
|
|
||||||
/*
|
/*
|
||||||
* These constants are from libhid/include/constants.h
|
* These constants are from libhid/include/constants.h
|
||||||
* and usage based on libhid/src/hid_exchange.c
|
* and usage based on libhid/src/hid_exchange.c
|
||||||
@ -14,8 +37,23 @@
|
|||||||
*/
|
*/
|
||||||
#define WEIGH_REPORT_SIZE 0x06
|
#define WEIGH_REPORT_SIZE 0x06
|
||||||
|
|
||||||
|
//
|
||||||
|
// **find_scale** takes a libusb device list and finds the first USB device
|
||||||
|
// that matches a device listed in scales.h.
|
||||||
|
//
|
||||||
static libusb_device* find_scale(libusb_device**);
|
static libusb_device* find_scale(libusb_device**);
|
||||||
|
//
|
||||||
|
// **print_scale_data** takes the 6-byte output from the scale and interprets
|
||||||
|
// it, printing out the result to the screen. It also returns a 1 if the
|
||||||
|
// program should read again (i.e. continue looping).
|
||||||
|
//
|
||||||
static int print_scale_data(char*);
|
static int print_scale_data(char*);
|
||||||
|
//
|
||||||
|
// **UNITS** is an array of all the unit abbreviations as set forth by *HID
|
||||||
|
// Point of Sale Usage Tables*, version 1.02, by the USB Implementers' Forum.
|
||||||
|
// The list is laid out so that the unit code returned by the scale is the
|
||||||
|
// index of its corresponding string.
|
||||||
|
//
|
||||||
const char* UNITS[13] = {
|
const char* UNITS[13] = {
|
||||||
"units", // unknown unit
|
"units", // unknown unit
|
||||||
"mg", // milligram
|
"mg", // milligram
|
||||||
@ -32,37 +70,72 @@ const char* UNITS[13] = {
|
|||||||
"lbs" // pound
|
"lbs" // pound
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// main
|
||||||
|
// ----
|
||||||
|
//
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
libusb_device **devs;
|
libusb_device **devs;
|
||||||
int r;
|
int r; // holds return codes
|
||||||
ssize_t cnt;
|
ssize_t cnt;
|
||||||
libusb_device* dev;
|
libusb_device* dev;
|
||||||
libusb_device_handle* handle;
|
libusb_device_handle* handle;
|
||||||
|
|
||||||
|
//
|
||||||
|
// We first try to init libusb.
|
||||||
|
//
|
||||||
r = libusb_init(NULL);
|
r = libusb_init(NULL);
|
||||||
|
//
|
||||||
|
// If `libusb_init` errored, then we quit immediately.
|
||||||
|
//
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
/*
|
//
|
||||||
* Get a list of USB devices
|
// Next, we try to get a list of USB devices on this computer.
|
||||||
*/
|
|
||||||
cnt = libusb_get_device_list(NULL, &devs);
|
cnt = libusb_get_device_list(NULL, &devs);
|
||||||
if (cnt < 0)
|
if (cnt < 0)
|
||||||
return (int) cnt;
|
return (int) cnt;
|
||||||
|
|
||||||
/*
|
//
|
||||||
* Look for a scale amongst the USB devices
|
// Once we have the list, we use **find_scale** to loop through and match
|
||||||
*/
|
// every device against the scales.h list. **find_scale** will return the
|
||||||
|
// first device that matches, or 0 if none of them matched.
|
||||||
|
//
|
||||||
dev = find_scale(devs);
|
dev = find_scale(devs);
|
||||||
|
if(dev == 0) {
|
||||||
|
fprintf(stderr, "No USB scale found on this computer.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
//
|
||||||
* Open a handle to this found scale
|
// Once we have a pointer to the USB scale in question, we open it.
|
||||||
*/
|
//
|
||||||
libusb_open(dev, &handle);
|
r = libusb_open(dev, &handle);
|
||||||
|
//
|
||||||
|
// Note that this requires that we have permission to access this device.
|
||||||
|
// If you get the "permission denied" error, check your udev rules.
|
||||||
|
//
|
||||||
|
if(r < 0) {
|
||||||
|
if(r == LIBUSB_ERROR_ACCESS) {
|
||||||
|
fprintf(stderr, "Permission denied to scale.\n");
|
||||||
|
}
|
||||||
|
else if(r == LIBUSB_ERROR_NO_DEVICE) {
|
||||||
|
fprintf(stderr, "Scale has been disconnected.\n");
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// On Linux, we typically need to detach the kernel driver so that we can
|
||||||
|
// handle this USB device. We are a userspace tool, after all.
|
||||||
|
//
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
libusb_detach_kernel_driver(handle, 0);
|
libusb_detach_kernel_driver(handle, 0);
|
||||||
#endif
|
#endif
|
||||||
|
//
|
||||||
|
// Finally, we can claim the interface to this device and begin I/O.
|
||||||
|
//
|
||||||
libusb_claim_interface(handle, 0);
|
libusb_claim_interface(handle, 0);
|
||||||
|
|
||||||
|
|
||||||
@ -74,12 +147,21 @@ int main(void)
|
|||||||
*/
|
*/
|
||||||
unsigned char data[WEIGH_REPORT_SIZE];
|
unsigned char data[WEIGH_REPORT_SIZE];
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
unsigned int res;
|
|
||||||
int continue_reading = 0;
|
int continue_reading = 0;
|
||||||
int scale_result = -1;
|
int scale_result = -1;
|
||||||
|
|
||||||
|
//
|
||||||
|
// We read data from the scale in an infinite loop, stopping when
|
||||||
|
// **print_scale_data** tells us that it's successfully gotten the weight
|
||||||
|
// from the scale, or if the scale or transmissions indicates an error.
|
||||||
|
//
|
||||||
for(;;) {
|
for(;;) {
|
||||||
res= libusb_interrupt_transfer(
|
//
|
||||||
|
// A `libusb_interrupt_transfer` of 6 bytes from the scale is the
|
||||||
|
// typical scale data packet, and the usage is laid out in *HID Point
|
||||||
|
// of Sale Usage Tables*, version 1.02.
|
||||||
|
//
|
||||||
|
r = libusb_interrupt_transfer(
|
||||||
handle,
|
handle,
|
||||||
//bmRequestType => direction: in, type: class,
|
//bmRequestType => direction: in, type: class,
|
||||||
// recipient: interface
|
// recipient: interface
|
||||||
@ -90,8 +172,11 @@ int main(void)
|
|||||||
&len,
|
&len,
|
||||||
10000 //timeout => 10 sec
|
10000 //timeout => 10 sec
|
||||||
);
|
);
|
||||||
|
//
|
||||||
if(res == 0) {
|
// If the data transfer succeeded, then we pass along the data we
|
||||||
|
// received tot **print_scale_data**.
|
||||||
|
//
|
||||||
|
if(r == 0) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
int i;
|
int i;
|
||||||
for(i = 0; i < WEIGH_REPORT_SIZE; i++) {
|
for(i = 0; i < WEIGH_REPORT_SIZE; i++) {
|
||||||
@ -109,11 +194,11 @@ int main(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
/*
|
// At the end, we make sure that we reattach the kernel driver that we
|
||||||
* Free the device handle, device list and other cleanup
|
// detached earlier, close the handle to the device, free the device list
|
||||||
* tasks
|
// that we retrieved, and exit libusb.
|
||||||
*/
|
//
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
libusb_attach_kernel_driver(handle, 0);
|
libusb_attach_kernel_driver(handle, 0);
|
||||||
#endif
|
#endif
|
||||||
@ -121,6 +206,10 @@ int main(void)
|
|||||||
libusb_free_device_list(devs, 1);
|
libusb_free_device_list(devs, 1);
|
||||||
libusb_exit(NULL);
|
libusb_exit(NULL);
|
||||||
|
|
||||||
|
//
|
||||||
|
// The return code will be 0 for success or -1 for errors (see
|
||||||
|
// `libusb_init` above if it's neither 0 nor -1).
|
||||||
|
//
|
||||||
return scale_result;
|
return scale_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,8 +227,17 @@ int main(void)
|
|||||||
//
|
//
|
||||||
static int print_scale_data(char* dat) {
|
static int print_scale_data(char* dat) {
|
||||||
|
|
||||||
|
//
|
||||||
|
// We keep around `lastStatus` so that we're not constantly printing the
|
||||||
|
// same status message while waiting for a weighing. If the status hasn't
|
||||||
|
// changed from last time, **print_scale_data** prints nothing.
|
||||||
|
//
|
||||||
static uint8_t lastStatus = 0;
|
static uint8_t lastStatus = 0;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Gently rip apart the scale's data packet according to *HID Point of Sale
|
||||||
|
// Usage Tables*.
|
||||||
|
//
|
||||||
uint8_t report = dat[0];
|
uint8_t report = dat[0];
|
||||||
uint8_t status = dat[1];
|
uint8_t status = dat[1];
|
||||||
uint8_t unit = dat[2];
|
uint8_t unit = dat[2];
|
||||||
@ -150,11 +248,19 @@ static int print_scale_data(char* dat) {
|
|||||||
weight = pow(weight, expt);
|
weight = pow(weight, expt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// The scale's first byte, its "report", is always 3.
|
||||||
|
//
|
||||||
if(report != 0x03) {
|
if(report != 0x03) {
|
||||||
fprintf(stderr, "Error reading scale data\n");
|
fprintf(stderr, "Error reading scale data\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Switch on the status byte given by the scale. Note that we make a
|
||||||
|
// distinction between statuses that we simply wait on, and statuses that
|
||||||
|
// cause us to stop (`return -1`).
|
||||||
|
//
|
||||||
switch(status) {
|
switch(status) {
|
||||||
case 0x01:
|
case 0x01:
|
||||||
fprintf(stderr, "Scale reports Fault\n");
|
fprintf(stderr, "Scale reports Fault\n");
|
||||||
@ -167,6 +273,11 @@ static int print_scale_data(char* dat) {
|
|||||||
if(status != lastStatus)
|
if(status != lastStatus)
|
||||||
fprintf(stderr, "Weighing...\n");
|
fprintf(stderr, "Weighing...\n");
|
||||||
break;
|
break;
|
||||||
|
//
|
||||||
|
// 0x04 is the only final, successful status, and it indicates that we
|
||||||
|
// have a finalized weight ready to print. Here is where we make use of
|
||||||
|
// the `UNITS` lookup table for unit names.
|
||||||
|
//
|
||||||
case 0x04:
|
case 0x04:
|
||||||
printf("%ld %s\n", weight, UNITS[unit]);
|
printf("%ld %s\n", weight, UNITS[unit]);
|
||||||
return 0;
|
return 0;
|
||||||
@ -203,7 +314,7 @@ static libusb_device* find_scale(libusb_device **devs)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
libusb_device* dev;
|
libusb_device* dev = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Loop through each usb device, and for each device, loop through the
|
* Loop through each usb device, and for each device, loop through the
|
||||||
|
Loading…
x
Reference in New Issue
Block a user