Gerry
2015-01-20 11:24:34 UTC
Sorry for the late reply but I had sent this to the pyusb-users-owner
list instead of this list. I had originally included the files as
attachments but the list stripped them off so now I am including them in
this email.
Hopefully someone can find a solution to this problem.
-------- Forwarded Message --------
Subject: My Python source code and another's C code
Date: Wed, 14 Jan 2015 19:09:35 +0800
From: Gerry <***@gerbreown.com>
To: pyusb-users-***@lists.sourceforge.net
Here is my Python code:
"#!/usr/bin/python
"""
Read a MagTek USB HID Swipe Reader in Linux. A description of this
code can be found at:
http://www.micahcarrick.com/credit-card-reader-pyusb.html
You must be using the new PyUSB 1.0 branch and not the 0.x branch.
Copyright (c) 2010 - Micah Carrick
"""
import sys
import usb.core
#import usb.backend.libusb1 as libusb1
import usb.util
VENDOR_ID = 0x24c0
PRODUCT_ID = 0x0003
DATA_SIZE = 50
# find the Acurite Weather Station
device = usb.core.find(idVendor=0x24c0, idProduct=0x0003)
if device is None:
sys.exit("Could not find Acurite weatherstation.")
#else:
# print("Device FOUND!!", device)
# fout = open('device.txt', 'w')
# d1 = str(device)
# fout.write(d1)
# fout.close
# make sure the hiddev kernel driver is not active
if device.is_kernel_driver_active(0):
try:
device.detach_kernel_driver(0)
except usb.core.USBError as e:
sys.exit("Could not detatch kernel driver: %s" % str(e))
# set configuration
try:
device.set_configuration()
device.reset()
except usb.core.USBError as e:
sys.exit("Could not set configuration: %s" % str(e))
endpoint = device[0][(0,0)][0]
#print ("Endpoint = ", endpoint)
#epa = endpoint.bEndpointAddress
#mps = endpoint.wMaxPacketSize
#print ("EPA = ", epa, "MPS= ", mps)
data = []
print ("Now starting to read...")
*This is where I am getting the errors. I originally had it as "d1 =
device.read(..." but after looking at the Pyusb.core code it said it
should be "d1 = read(device..." Both ways give the same errors.
Hopefully you can find the problem so I can read my weather station...*
d1 = read(device, endpoint.bEndpointAddress, endpoint.wMaxPacketSize)
print ("D1 =",d1)
'''while 1:
try:
#data += device.read(endpoint.bEndpointAddress,
endpoint.wMaxPacketSize)
#print("Data = ",data)
data += device.read(0x81, 8)
print ("Reading...")
except usb.core.USBError as e:
if e.args == ('Operation timed out',): #and swiped:
if len(data) < DATA_SIZE:
print ("Bad swipe, try again. (%d bytes)") % len(data)
print ("Data: %s" % ''.join(map(chr, data)))
data = []
continue
else:
print ("Data = ",data)
break # we got it!
'''
The other person's C code:
"/*
Documentation at desert-home.com
Experimentation with a USB interface to the Acu-Rite 5 in 1
Weatherstation
specifically for the Raspberry Pi. The code may work on other
systems, but I
don't have one of those. Contrary to other folk's thinking, I
don't care if
this ever runs on a Windows PC or an Apple anything.
I specifically used a model 2032 display with the sensor that I picked
up at one of those warehouse stores. The display has a usb plug on
it and
I thought it might be possible to read the usb port and massage the
data myself.
This code represents the result of that effort.
I gathered ideas from all over the web. I use the latest (for this
second)
libusb and about a thousand examples of various things that other
people have
done and posted about. Frankly, I simply can't remember all of
them, so please,
don't be offended if you see your ideas somewhere in here and it
isn't attributed.
I simply lost track of where I found what.
This module relies on libusb version 1.0.19 which, at this time,
can only be
compiled from source on the raspberry pi.
Because there likely to be a version of libusb and the associated
header file
on a Pi, use the command line below to build it since the build of
libusb-1.0.19
places things in /usr/local
cc -o weatherstation weatherstation.c -L/usr/local/lib -lusb-1.0
*
use ldd weatherstation to check which libraries are linked in.
If you still have trouble with compilation, remember that cc has a -v
parameter that can help you unwind what is happening.
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <sys/time.h>
#include <libusb-1.0/libusb.h>
// The vendor id and product number for the AcuRite 5 in 1 weather head.
#define VENDOR 0x24c0
#define PRODUCT 0x0003
// I store things about the weather device USB connection here.
struct {
libusb_device *device;
libusb_device_handle *handle;
int verbose;
} weatherStation;
// These are the sensors the the 5 in 1 weather head provides
struct {
float windSpeed;
time_t wsTime;
int windDirection;
time_t wdTime;
float temperature;
time_t tTime;
int humidity;
time_t hTime;
int rainCounter;
time_t rcTime;
} weatherData;
// This is just a function prototype for the compiler
void closeUpAndLeave();
// I want to catch control-C and close down gracefully
void sig_handler(int signo)
{
if (signo == SIGINT)
fprintf(stderr,"Shutting down ...\n");
closeUpAndLeave();
}
/*
This tiny thing simply takes the data and prints it so we can see it
*/
// Array to translate the integer direction provided to text
char *Direction[] = {
"NNW",
"NW ",
"WNW",
"W ",
"WSW",
"SW ",
"SSW",
"S ",
"SSE",
"SE ",
"ESE",
"E ",
"ENE",
"NE ",
"NNE",
"N " };
void showit(){
fprintf(stdout, "{\"windSpeed\":{\"WS\":\"%.1f\",\"t\":\"%d\"},"
"\"windDirection\":{\"WD\":\"%s\",\"t\":\"%d\"},"
"\"temperature\":{\"T\":\"%.1f\",\"t\":\"%d\"},"
"\"humidity\":{\"H\":\"%d\",\"t\":\"%d\"},"
"\"rainCounter\":{\"RC\":\"%d\",\"t\":\"%d\"}}\n",
weatherData.windSpeed, weatherData.wsTime,
Direction[weatherData.windDirection],weatherData.wdTime,
weatherData.temperature, weatherData.tTime,
weatherData.humidity, weatherData.hTime,
weatherData.rainCounter, weatherData.rcTime);
fflush(stdout);
}
/*
This code translates the data from the 5 in 1 sensors to something
that can be used by a human.
*/
float getWindSpeed(char *data){
int leftSide = (data[3] & 0x1f) << 3;
int rightSide = data[4] & 0x70 >> 4;
// Yes, I use mph, never got used to kilometers.
return((float)(leftSide | rightSide) * 0.62);
}
int getWindDirection(char *data){
return(data[4] & 0x0f);
}
float getTemp(char *data){
// This item spans bytes, have to reconstruct it
int leftSide = (data[4] & 0x0f) << 7;
int rightSide = data[5] & 0x7f;
float combined = leftSide | rightSide;
return((combined - 400) / 10.0);
}
int getHumidity(char *data){
int howWet = data[6] &0x7f;
return(howWet);
}
int getRainCount(char *data){
int count = data[6] &0x7f;
return(count);
}
// Now that I have the data from the station, do something useful with it.
void decode(char *data, int length, int noisy){
//int i;
//for(i=0; i<length; i++){
// fprintf(stderr,"%0.2X ",data[i]);
//}
//fprintf(stderr,"\n");
time_t seconds = time (NULL);
//There are two varieties of data, both of them have wind speed
// first variety of the data
if ((data[2] & 0x0f) == 1){ // this has wind speed, direction and
rainfall
if(noisy)
fprintf(stderr,"Wind Speed: %.1f ",getWindSpeed(data));
weatherData.windSpeed = getWindSpeed(data);
weatherData.wsTime = seconds;
if(noisy)
fprintf(stderr,"Wind Direction: %s
",Direction[getWindDirection(data)]);
weatherData.wdTime = seconds;
weatherData.windDirection = getWindDirection(data);
if(noisy){
fprintf(stderr,"Rain Counter: %d ",getRainCount(data));
fprintf(stderr,"\n");
}
weatherData.rainCounter = getRainCount(data);
weatherData.rcTime = seconds;
}
// this is the other variety
if ((data[2] & 0x0f) == 8){ // this has wind speed, temp and
relative humidity
if(noisy)
fprintf(stderr,"Wind Speed: %.1f ",getWindSpeed(data));
weatherData.windSpeed = getWindSpeed(data);
weatherData.wsTime = seconds;
if(noisy)
fprintf(stderr,"Temperature: %.1f ",getTemp(data));
weatherData.temperature = getTemp(data);
weatherData.tTime = seconds;
if(noisy){
fprintf(stderr,"Humidity: %d ", getHumidity(data));
fprintf(stderr,"\n");
}
weatherData.humidity = getHumidity(data);
weatherData.hTime = seconds;
}
}
/*
This code is related to dealing with the USB device
*/
// This searches the USB bus tree to find the device
int findDevice(libusb_device **devs)
{
libusb_device *dev;
int err = 0, i = 0, j = 0;
uint8_t path[8];
while ((dev = devs[i++]) != NULL) {
struct libusb_device_descriptor desc;
int r = libusb_get_device_descriptor(dev, &desc);
if (r < 0) {
fprintf(stderr,"Couldn't get device descriptor, %s\n",
libusb_strerror(err));
return(1);
}
fprintf(stderr,"%04x:%04x (bus %d, device %d)",
desc.idVendor, desc.idProduct,
libusb_get_bus_number(dev), libusb_get_device_address(dev));
//r = libusb_get_port_numbers(dev, path, sizeof(path));
//if (r > 0) {
// fprintf(stderr," path: %d", path[0]);
// for (j = 1; j < r; j++)
// fprintf(stderr,".%d", path[j]);
//}
//fprintf(stderr,"\n");
if (desc.idVendor == VENDOR && desc.idProduct == PRODUCT){
fprintf(stderr,"Found the one I want\n");
weatherStation.device = dev;
return (1);
}
}
return(0);
}
// to handle testing and try to be clean about closing the USB device,
// I'll catch the signal and close off.
void closeUpAndLeave(){
//OK, done with it, close off and let it go.
fprintf(stderr,"Done with device, release and close it\n");
int err = libusb_release_interface(weatherStation.handle, 0);
//release the claimed interface
if(err) {
fprintf(stderr,"Couldn't release interface, %s\n",
libusb_strerror(err));
exit(1);
}
libusb_close(weatherStation.handle);
libusb_exit(NULL);
exit(0);
}
// This is where I read the USB device to get the latest data.
unsigned char data[50]; // where we want the data to go
int getit(int whichOne, int noisy){
int actual; // how many bytes were actually read
// The second parameter is bmRequestType and is a bitfield
// See http://www.beyondlogic.org/usbnutshell/usb6.shtml
// for the definitions of the various bits. With libusb, the
// #defines for these are at:
//http://libusb.sourceforge.net/api-1.0/group__misc.html#gga0b0933ae70744726cde11254c39fac91a20eca62c34d2d25be7e1776510184209
*This is where the data is being read from the device.*
actual = libusb_control_transfer(weatherStation.handle,
LIBUSB_REQUEST_TYPE_CLASS |
LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_IN,
//These bytes were stolen with a USB sniffer
0x01,0x0100+whichOne,0,
data, 50, 10000);
if (actual < 0){
fprintf(stderr,"Read didn't work for report %d, %s\n",
whichOne, libusb_strerror(actual));
}
else {
// If you want both of the reports that the station provides,
// just allow for it. Right this second, I've found every thing
// I need in report 1. When I look further at report 2, this will
// change
/*fprintf(stderr,"R%d:%d:", whichOne, actual);
int i;
for(i=0; i<actual; i++){
fprintf(stderr,"%0.2X ",data[i]);
}
fprintf(stderr,"\n"); */
if (whichOne == 1)
// The actual data starts after the first byte
// The first byte is the report number returned by
// the usb read.
decode(&data[1], actual-1, noisy);
}
}
// I do several things here that aren't strictly necessary. As I
learned about
// libusb, I tried things and also used various techniques to learn
about the
// weatherstation's implementation. I left a lot of it in here in case
I needed to
// use it later. Someone may find it useful to hack into some other device.
int main(int argc, char **argv)
{
char *usage = {"usage: %s -u -n\n"};
int libusbDebug = 0; //This will turn on the DEBUG for libusb
int noisy = 0; //This will print the packets as they come in
libusb_device **devs;
int r, err, c;
ssize_t cnt;
while ((c = getopt (argc, argv, "unh")) != -1)
switch (c){
case 'u':
libusbDebug = 1;
break;
case 'n':
noisy = 1;
break;
case 'h':
fprintf(stderr, usage, argv[0]);
case '?':
exit(1);
default:
exit(1);
}
fprintf (stderr,"libusbDebug = %d, noisy = %d\n", libusbDebug, noisy);
if (signal(SIGINT, sig_handler) == SIG_ERR)
fprintf(stderr,"Couldn't set up signal handler\n");
err = libusb_init(NULL);
if (err < 0){
fprintf(stderr,"Couldn't init usblib, %s\n", libusb_strerror(err));
exit(1);
}
// This is where you can get debug output from libusb.
// just set it to LIBUSB_LOG_LEVEL_DEBUG
if (libusbDebug)
libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_DEBUG);
else
libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_INFO);
cnt = libusb_get_device_list(NULL, &devs);
if (cnt < 0){
fprintf(stderr,"Couldn't get device list, %s\n",
libusb_strerror(err));
exit(1);
}
// got get the device; the device handle is saved in weatherStation
struct.
if (!findDevice(devs)){
fprintf(stderr,"Couldn't find the device\n");
exit(1);
}
// Now I've found the weather station and can start to try stuff
// So, I'll get the device descriptor
struct libusb_device_descriptor deviceDesc;
err = libusb_get_device_descriptor(weatherStation.device, &deviceDesc);
if (err){
fprintf(stderr,"Couldn't get device descriptor, %s\n",
libusb_strerror(err));
exit(1);
}
fprintf(stderr,"Got the device descriptor back\n");
// Open the device and save the handle in the weatherStation struct
err = libusb_open(weatherStation.device, &weatherStation.handle);
if (err){
fprintf(stderr,"Open failed, %s\n", libusb_strerror(err));
exit(1);
}
fprintf(stderr,"I was able to open it\n");
// There's a bug in either the usb library, the linux driver or the
// device itself. I suspect the usb driver, but don't know for sure.
// If you plug and unplug the weather station a few times, it will stop
// responding to reads. It also exhibits some strange behaviour to
// getting the configuration. I found out after a couple of days of
// experimenting that doing a clear-halt on the device while before it
// was opened it would clear the problem. So, I have one here and a
// little further down after it has been opened.
fprintf(stderr,"trying clear halt on endpoint %X ... ", 0x81);
// err = libusb_clear_halt(weatherStation.handle, 0x81);
if (err){
fprintf(stderr,"clear halt crapped, %s Bug Detector\n",
libusb_strerror(err));;
}
else {
fprintf(stderr,"OK\n");
}
// Now that it's opened, I can free the list of all devices
libusb_free_device_list(devs, 1); // Documentation says to get rid
of the list
// Once I have the device I need
fprintf(stderr,"Released the device list\n");
// Now I have to check to see if the kernal using udev has attached
// a driver to the device. If it has, it has to be detached so I can
// use the device.
if(libusb_kernel_driver_active(weatherStation.handle, 0) == 1) {
//find out if kernel driver is attached
fprintf(stderr,"Kernal driver active\n");
if(libusb_detach_kernel_driver(weatherStation.handle, 0) == 0)
//detach it
fprintf(stderr,"Kernel Driver Detached!\n");
}
int activeConfig;
err =libusb_get_configuration (weatherStation.handle, &activeConfig);
if (err){
fprintf(stderr,"Can't get current active configuration, %s\n",
libusb_strerror(err));;
exit(1);
}
fprintf(stderr,"Currently active configuration is %d\n", activeConfig);
if(activeConfig != 1){
err = libusb_set_configuration (weatherStation.handle, 1);
if (err){
fprintf(stderr,"Cannot set configuration, %s\n",
libusb_strerror(err));;
exit(1);
}
fprintf(stderr,"Just did the set configuration\n");
}
err = libusb_claim_interface(weatherStation.handle, 0); //claim
interface 0 (the first) of device (mine had jsut 1)
if(err) {
fprintf(stderr,"Cannot claim interface, %s\n",
libusb_strerror(err));
exit(1);
}
fprintf(stderr,"Claimed Interface\n");
fprintf(stderr,"Number of configurations:
%d\n",deviceDesc.bNumConfigurations);
struct libusb_config_descriptor *config;
libusb_get_config_descriptor(weatherStation.device, 0, &config);
fprintf(stderr,"Number of Interfaces:
%d\n",(int)config->bNumInterfaces);
// I know, the device only has one interface, but I wanted this code
// to serve as a reference for some future hack into some other device,
// so I put this loop to show the other interfaces that may
// be there. And, like most of this module, I stole the ideas from
// somewhere, but I can't remember where (I guess it's google overload)
const struct libusb_interface *inter;
const struct libusb_interface_descriptor *interdesc;
const struct libusb_endpoint_descriptor *epdesc;
int i, j, k;
for(i=0; i<(int)config->bNumInterfaces; i++) {
inter = &config->interface[i];
fprintf(stderr,"Number of alternate settings: %d\n",
inter->num_altsetting);
for(j=0; j < inter->num_altsetting; j++) {
interdesc = &inter->altsetting[j];
fprintf(stderr,"Interface Number: %d\n",
(int)interdesc->bInterfaceNumber);
fprintf(stderr,"Number of endpoints: %d\n",
(int)interdesc->bNumEndpoints);
for(k=0; k < (int)interdesc->bNumEndpoints; k++) {
epdesc = &interdesc->endpoint[k];
fprintf(stderr,"Descriptor Type:
%d\n",(int)epdesc->bDescriptorType);
fprintf(stderr,"Endpoint Address:
0x%0.2X\n",(int)epdesc->bEndpointAddress);
// Below is how to tell which direction the
// endpoint is supposed to work. It's the high order bit
// in the endpoint address. I guess they wanted to
hide it.
fprintf(stderr," Direction is ");
if ((int)epdesc->bEndpointAddress & LIBUSB_ENDPOINT_IN
!= 0)
fprintf(stderr," In (device to host)");
else
fprintf(stderr," Out (host to device)");
fprintf(stderr,"\n");
}
}
}
fprintf(stderr,"trying clear halt on endpoint %X ... ",
(int)epdesc->bEndpointAddress);
// err = libusb_clear_halt(weatherStation.handle,
(int)epdesc->bEndpointAddress);
if (err){
fprintf(stderr,"clear halt crapped, %s SHUCKS\n",
libusb_strerror(err));;
closeUpAndLeave();
}
else {
fprintf(stderr,"OK\n");
}
// So, for the weather station we now know it has one endpoint and
it is set to
// send data to the host. Now we can experiment with that.
//
// I don't want to just hang up and read the reports as fast as I
can, so
// I'll space them out a bit. It's weather, and it doesn't change
very fast.
int tickcounter= 0;
while(1){
sleep(1);
if(tickcounter++ % 10 == 0){
getit(1, noisy);
}
if(tickcounter % 30 == 0){
getit(2, noisy);
}
if (tickcounter % 15 == 0){
showit();
}
}
}"
list instead of this list. I had originally included the files as
attachments but the list stripped them off so now I am including them in
this email.
Hopefully someone can find a solution to this problem.
-------- Forwarded Message --------
Subject: My Python source code and another's C code
Date: Wed, 14 Jan 2015 19:09:35 +0800
From: Gerry <***@gerbreown.com>
To: pyusb-users-***@lists.sourceforge.net
Here is my Python code:
"#!/usr/bin/python
"""
Read a MagTek USB HID Swipe Reader in Linux. A description of this
code can be found at:
http://www.micahcarrick.com/credit-card-reader-pyusb.html
You must be using the new PyUSB 1.0 branch and not the 0.x branch.
Copyright (c) 2010 - Micah Carrick
"""
import sys
import usb.core
#import usb.backend.libusb1 as libusb1
import usb.util
VENDOR_ID = 0x24c0
PRODUCT_ID = 0x0003
DATA_SIZE = 50
# find the Acurite Weather Station
device = usb.core.find(idVendor=0x24c0, idProduct=0x0003)
if device is None:
sys.exit("Could not find Acurite weatherstation.")
#else:
# print("Device FOUND!!", device)
# fout = open('device.txt', 'w')
# d1 = str(device)
# fout.write(d1)
# fout.close
# make sure the hiddev kernel driver is not active
if device.is_kernel_driver_active(0):
try:
device.detach_kernel_driver(0)
except usb.core.USBError as e:
sys.exit("Could not detatch kernel driver: %s" % str(e))
# set configuration
try:
device.set_configuration()
device.reset()
except usb.core.USBError as e:
sys.exit("Could not set configuration: %s" % str(e))
endpoint = device[0][(0,0)][0]
#print ("Endpoint = ", endpoint)
#epa = endpoint.bEndpointAddress
#mps = endpoint.wMaxPacketSize
#print ("EPA = ", epa, "MPS= ", mps)
data = []
print ("Now starting to read...")
*This is where I am getting the errors. I originally had it as "d1 =
device.read(..." but after looking at the Pyusb.core code it said it
should be "d1 = read(device..." Both ways give the same errors.
Hopefully you can find the problem so I can read my weather station...*
d1 = read(device, endpoint.bEndpointAddress, endpoint.wMaxPacketSize)
print ("D1 =",d1)
'''while 1:
try:
#data += device.read(endpoint.bEndpointAddress,
endpoint.wMaxPacketSize)
#print("Data = ",data)
data += device.read(0x81, 8)
print ("Reading...")
except usb.core.USBError as e:
if e.args == ('Operation timed out',): #and swiped:
if len(data) < DATA_SIZE:
print ("Bad swipe, try again. (%d bytes)") % len(data)
print ("Data: %s" % ''.join(map(chr, data)))
data = []
continue
else:
print ("Data = ",data)
break # we got it!
'''
The other person's C code:
"/*
Documentation at desert-home.com
Experimentation with a USB interface to the Acu-Rite 5 in 1
Weatherstation
specifically for the Raspberry Pi. The code may work on other
systems, but I
don't have one of those. Contrary to other folk's thinking, I
don't care if
this ever runs on a Windows PC or an Apple anything.
I specifically used a model 2032 display with the sensor that I picked
up at one of those warehouse stores. The display has a usb plug on
it and
I thought it might be possible to read the usb port and massage the
data myself.
This code represents the result of that effort.
I gathered ideas from all over the web. I use the latest (for this
second)
libusb and about a thousand examples of various things that other
people have
done and posted about. Frankly, I simply can't remember all of
them, so please,
don't be offended if you see your ideas somewhere in here and it
isn't attributed.
I simply lost track of where I found what.
This module relies on libusb version 1.0.19 which, at this time,
can only be
compiled from source on the raspberry pi.
Because there likely to be a version of libusb and the associated
header file
on a Pi, use the command line below to build it since the build of
libusb-1.0.19
places things in /usr/local
cc -o weatherstation weatherstation.c -L/usr/local/lib -lusb-1.0
*
use ldd weatherstation to check which libraries are linked in.
If you still have trouble with compilation, remember that cc has a -v
parameter that can help you unwind what is happening.
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <sys/time.h>
#include <libusb-1.0/libusb.h>
// The vendor id and product number for the AcuRite 5 in 1 weather head.
#define VENDOR 0x24c0
#define PRODUCT 0x0003
// I store things about the weather device USB connection here.
struct {
libusb_device *device;
libusb_device_handle *handle;
int verbose;
} weatherStation;
// These are the sensors the the 5 in 1 weather head provides
struct {
float windSpeed;
time_t wsTime;
int windDirection;
time_t wdTime;
float temperature;
time_t tTime;
int humidity;
time_t hTime;
int rainCounter;
time_t rcTime;
} weatherData;
// This is just a function prototype for the compiler
void closeUpAndLeave();
// I want to catch control-C and close down gracefully
void sig_handler(int signo)
{
if (signo == SIGINT)
fprintf(stderr,"Shutting down ...\n");
closeUpAndLeave();
}
/*
This tiny thing simply takes the data and prints it so we can see it
*/
// Array to translate the integer direction provided to text
char *Direction[] = {
"NNW",
"NW ",
"WNW",
"W ",
"WSW",
"SW ",
"SSW",
"S ",
"SSE",
"SE ",
"ESE",
"E ",
"ENE",
"NE ",
"NNE",
"N " };
void showit(){
fprintf(stdout, "{\"windSpeed\":{\"WS\":\"%.1f\",\"t\":\"%d\"},"
"\"windDirection\":{\"WD\":\"%s\",\"t\":\"%d\"},"
"\"temperature\":{\"T\":\"%.1f\",\"t\":\"%d\"},"
"\"humidity\":{\"H\":\"%d\",\"t\":\"%d\"},"
"\"rainCounter\":{\"RC\":\"%d\",\"t\":\"%d\"}}\n",
weatherData.windSpeed, weatherData.wsTime,
Direction[weatherData.windDirection],weatherData.wdTime,
weatherData.temperature, weatherData.tTime,
weatherData.humidity, weatherData.hTime,
weatherData.rainCounter, weatherData.rcTime);
fflush(stdout);
}
/*
This code translates the data from the 5 in 1 sensors to something
that can be used by a human.
*/
float getWindSpeed(char *data){
int leftSide = (data[3] & 0x1f) << 3;
int rightSide = data[4] & 0x70 >> 4;
// Yes, I use mph, never got used to kilometers.
return((float)(leftSide | rightSide) * 0.62);
}
int getWindDirection(char *data){
return(data[4] & 0x0f);
}
float getTemp(char *data){
// This item spans bytes, have to reconstruct it
int leftSide = (data[4] & 0x0f) << 7;
int rightSide = data[5] & 0x7f;
float combined = leftSide | rightSide;
return((combined - 400) / 10.0);
}
int getHumidity(char *data){
int howWet = data[6] &0x7f;
return(howWet);
}
int getRainCount(char *data){
int count = data[6] &0x7f;
return(count);
}
// Now that I have the data from the station, do something useful with it.
void decode(char *data, int length, int noisy){
//int i;
//for(i=0; i<length; i++){
// fprintf(stderr,"%0.2X ",data[i]);
//}
//fprintf(stderr,"\n");
time_t seconds = time (NULL);
//There are two varieties of data, both of them have wind speed
// first variety of the data
if ((data[2] & 0x0f) == 1){ // this has wind speed, direction and
rainfall
if(noisy)
fprintf(stderr,"Wind Speed: %.1f ",getWindSpeed(data));
weatherData.windSpeed = getWindSpeed(data);
weatherData.wsTime = seconds;
if(noisy)
fprintf(stderr,"Wind Direction: %s
",Direction[getWindDirection(data)]);
weatherData.wdTime = seconds;
weatherData.windDirection = getWindDirection(data);
if(noisy){
fprintf(stderr,"Rain Counter: %d ",getRainCount(data));
fprintf(stderr,"\n");
}
weatherData.rainCounter = getRainCount(data);
weatherData.rcTime = seconds;
}
// this is the other variety
if ((data[2] & 0x0f) == 8){ // this has wind speed, temp and
relative humidity
if(noisy)
fprintf(stderr,"Wind Speed: %.1f ",getWindSpeed(data));
weatherData.windSpeed = getWindSpeed(data);
weatherData.wsTime = seconds;
if(noisy)
fprintf(stderr,"Temperature: %.1f ",getTemp(data));
weatherData.temperature = getTemp(data);
weatherData.tTime = seconds;
if(noisy){
fprintf(stderr,"Humidity: %d ", getHumidity(data));
fprintf(stderr,"\n");
}
weatherData.humidity = getHumidity(data);
weatherData.hTime = seconds;
}
}
/*
This code is related to dealing with the USB device
*/
// This searches the USB bus tree to find the device
int findDevice(libusb_device **devs)
{
libusb_device *dev;
int err = 0, i = 0, j = 0;
uint8_t path[8];
while ((dev = devs[i++]) != NULL) {
struct libusb_device_descriptor desc;
int r = libusb_get_device_descriptor(dev, &desc);
if (r < 0) {
fprintf(stderr,"Couldn't get device descriptor, %s\n",
libusb_strerror(err));
return(1);
}
fprintf(stderr,"%04x:%04x (bus %d, device %d)",
desc.idVendor, desc.idProduct,
libusb_get_bus_number(dev), libusb_get_device_address(dev));
//r = libusb_get_port_numbers(dev, path, sizeof(path));
//if (r > 0) {
// fprintf(stderr," path: %d", path[0]);
// for (j = 1; j < r; j++)
// fprintf(stderr,".%d", path[j]);
//}
//fprintf(stderr,"\n");
if (desc.idVendor == VENDOR && desc.idProduct == PRODUCT){
fprintf(stderr,"Found the one I want\n");
weatherStation.device = dev;
return (1);
}
}
return(0);
}
// to handle testing and try to be clean about closing the USB device,
// I'll catch the signal and close off.
void closeUpAndLeave(){
//OK, done with it, close off and let it go.
fprintf(stderr,"Done with device, release and close it\n");
int err = libusb_release_interface(weatherStation.handle, 0);
//release the claimed interface
if(err) {
fprintf(stderr,"Couldn't release interface, %s\n",
libusb_strerror(err));
exit(1);
}
libusb_close(weatherStation.handle);
libusb_exit(NULL);
exit(0);
}
// This is where I read the USB device to get the latest data.
unsigned char data[50]; // where we want the data to go
int getit(int whichOne, int noisy){
int actual; // how many bytes were actually read
// The second parameter is bmRequestType and is a bitfield
// See http://www.beyondlogic.org/usbnutshell/usb6.shtml
// for the definitions of the various bits. With libusb, the
// #defines for these are at:
//http://libusb.sourceforge.net/api-1.0/group__misc.html#gga0b0933ae70744726cde11254c39fac91a20eca62c34d2d25be7e1776510184209
*This is where the data is being read from the device.*
actual = libusb_control_transfer(weatherStation.handle,
LIBUSB_REQUEST_TYPE_CLASS |
LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_IN,
//These bytes were stolen with a USB sniffer
0x01,0x0100+whichOne,0,
data, 50, 10000);
if (actual < 0){
fprintf(stderr,"Read didn't work for report %d, %s\n",
whichOne, libusb_strerror(actual));
}
else {
// If you want both of the reports that the station provides,
// just allow for it. Right this second, I've found every thing
// I need in report 1. When I look further at report 2, this will
// change
/*fprintf(stderr,"R%d:%d:", whichOne, actual);
int i;
for(i=0; i<actual; i++){
fprintf(stderr,"%0.2X ",data[i]);
}
fprintf(stderr,"\n"); */
if (whichOne == 1)
// The actual data starts after the first byte
// The first byte is the report number returned by
// the usb read.
decode(&data[1], actual-1, noisy);
}
}
// I do several things here that aren't strictly necessary. As I
learned about
// libusb, I tried things and also used various techniques to learn
about the
// weatherstation's implementation. I left a lot of it in here in case
I needed to
// use it later. Someone may find it useful to hack into some other device.
int main(int argc, char **argv)
{
char *usage = {"usage: %s -u -n\n"};
int libusbDebug = 0; //This will turn on the DEBUG for libusb
int noisy = 0; //This will print the packets as they come in
libusb_device **devs;
int r, err, c;
ssize_t cnt;
while ((c = getopt (argc, argv, "unh")) != -1)
switch (c){
case 'u':
libusbDebug = 1;
break;
case 'n':
noisy = 1;
break;
case 'h':
fprintf(stderr, usage, argv[0]);
case '?':
exit(1);
default:
exit(1);
}
fprintf (stderr,"libusbDebug = %d, noisy = %d\n", libusbDebug, noisy);
if (signal(SIGINT, sig_handler) == SIG_ERR)
fprintf(stderr,"Couldn't set up signal handler\n");
err = libusb_init(NULL);
if (err < 0){
fprintf(stderr,"Couldn't init usblib, %s\n", libusb_strerror(err));
exit(1);
}
// This is where you can get debug output from libusb.
// just set it to LIBUSB_LOG_LEVEL_DEBUG
if (libusbDebug)
libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_DEBUG);
else
libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_INFO);
cnt = libusb_get_device_list(NULL, &devs);
if (cnt < 0){
fprintf(stderr,"Couldn't get device list, %s\n",
libusb_strerror(err));
exit(1);
}
// got get the device; the device handle is saved in weatherStation
struct.
if (!findDevice(devs)){
fprintf(stderr,"Couldn't find the device\n");
exit(1);
}
// Now I've found the weather station and can start to try stuff
// So, I'll get the device descriptor
struct libusb_device_descriptor deviceDesc;
err = libusb_get_device_descriptor(weatherStation.device, &deviceDesc);
if (err){
fprintf(stderr,"Couldn't get device descriptor, %s\n",
libusb_strerror(err));
exit(1);
}
fprintf(stderr,"Got the device descriptor back\n");
// Open the device and save the handle in the weatherStation struct
err = libusb_open(weatherStation.device, &weatherStation.handle);
if (err){
fprintf(stderr,"Open failed, %s\n", libusb_strerror(err));
exit(1);
}
fprintf(stderr,"I was able to open it\n");
// There's a bug in either the usb library, the linux driver or the
// device itself. I suspect the usb driver, but don't know for sure.
// If you plug and unplug the weather station a few times, it will stop
// responding to reads. It also exhibits some strange behaviour to
// getting the configuration. I found out after a couple of days of
// experimenting that doing a clear-halt on the device while before it
// was opened it would clear the problem. So, I have one here and a
// little further down after it has been opened.
fprintf(stderr,"trying clear halt on endpoint %X ... ", 0x81);
// err = libusb_clear_halt(weatherStation.handle, 0x81);
if (err){
fprintf(stderr,"clear halt crapped, %s Bug Detector\n",
libusb_strerror(err));;
}
else {
fprintf(stderr,"OK\n");
}
// Now that it's opened, I can free the list of all devices
libusb_free_device_list(devs, 1); // Documentation says to get rid
of the list
// Once I have the device I need
fprintf(stderr,"Released the device list\n");
// Now I have to check to see if the kernal using udev has attached
// a driver to the device. If it has, it has to be detached so I can
// use the device.
if(libusb_kernel_driver_active(weatherStation.handle, 0) == 1) {
//find out if kernel driver is attached
fprintf(stderr,"Kernal driver active\n");
if(libusb_detach_kernel_driver(weatherStation.handle, 0) == 0)
//detach it
fprintf(stderr,"Kernel Driver Detached!\n");
}
int activeConfig;
err =libusb_get_configuration (weatherStation.handle, &activeConfig);
if (err){
fprintf(stderr,"Can't get current active configuration, %s\n",
libusb_strerror(err));;
exit(1);
}
fprintf(stderr,"Currently active configuration is %d\n", activeConfig);
if(activeConfig != 1){
err = libusb_set_configuration (weatherStation.handle, 1);
if (err){
fprintf(stderr,"Cannot set configuration, %s\n",
libusb_strerror(err));;
exit(1);
}
fprintf(stderr,"Just did the set configuration\n");
}
err = libusb_claim_interface(weatherStation.handle, 0); //claim
interface 0 (the first) of device (mine had jsut 1)
if(err) {
fprintf(stderr,"Cannot claim interface, %s\n",
libusb_strerror(err));
exit(1);
}
fprintf(stderr,"Claimed Interface\n");
fprintf(stderr,"Number of configurations:
%d\n",deviceDesc.bNumConfigurations);
struct libusb_config_descriptor *config;
libusb_get_config_descriptor(weatherStation.device, 0, &config);
fprintf(stderr,"Number of Interfaces:
%d\n",(int)config->bNumInterfaces);
// I know, the device only has one interface, but I wanted this code
// to serve as a reference for some future hack into some other device,
// so I put this loop to show the other interfaces that may
// be there. And, like most of this module, I stole the ideas from
// somewhere, but I can't remember where (I guess it's google overload)
const struct libusb_interface *inter;
const struct libusb_interface_descriptor *interdesc;
const struct libusb_endpoint_descriptor *epdesc;
int i, j, k;
for(i=0; i<(int)config->bNumInterfaces; i++) {
inter = &config->interface[i];
fprintf(stderr,"Number of alternate settings: %d\n",
inter->num_altsetting);
for(j=0; j < inter->num_altsetting; j++) {
interdesc = &inter->altsetting[j];
fprintf(stderr,"Interface Number: %d\n",
(int)interdesc->bInterfaceNumber);
fprintf(stderr,"Number of endpoints: %d\n",
(int)interdesc->bNumEndpoints);
for(k=0; k < (int)interdesc->bNumEndpoints; k++) {
epdesc = &interdesc->endpoint[k];
fprintf(stderr,"Descriptor Type:
%d\n",(int)epdesc->bDescriptorType);
fprintf(stderr,"Endpoint Address:
0x%0.2X\n",(int)epdesc->bEndpointAddress);
// Below is how to tell which direction the
// endpoint is supposed to work. It's the high order bit
// in the endpoint address. I guess they wanted to
hide it.
fprintf(stderr," Direction is ");
if ((int)epdesc->bEndpointAddress & LIBUSB_ENDPOINT_IN
!= 0)
fprintf(stderr," In (device to host)");
else
fprintf(stderr," Out (host to device)");
fprintf(stderr,"\n");
}
}
}
fprintf(stderr,"trying clear halt on endpoint %X ... ",
(int)epdesc->bEndpointAddress);
// err = libusb_clear_halt(weatherStation.handle,
(int)epdesc->bEndpointAddress);
if (err){
fprintf(stderr,"clear halt crapped, %s SHUCKS\n",
libusb_strerror(err));;
closeUpAndLeave();
}
else {
fprintf(stderr,"OK\n");
}
// So, for the weather station we now know it has one endpoint and
it is set to
// send data to the host. Now we can experiment with that.
//
// I don't want to just hang up and read the reports as fast as I
can, so
// I'll space them out a bit. It's weather, and it doesn't change
very fast.
int tickcounter= 0;
while(1){
sleep(1);
if(tickcounter++ % 10 == 0){
getit(1, noisy);
}
if(tickcounter % 30 == 0){
getit(2, noisy);
}
if (tickcounter % 15 == 0){
showit();
}
}
}"