diff --git a/.gitignore b/.gitignore index d18fbf3..ee159ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ src/pcsensor +src/sensorapi.o +*~ diff --git a/src/Makefile b/src/Makefile index 992b41c..b36d5dc 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,12 +1,29 @@ -all: pcsensor -CFLAGS = -O2 -Wall -pcsensor: pcsensor.c - ${CC} -DUNIT_TEST -o $@ $^ -lusb +CC := gcc +CFLAGS := -g -O2 -Wall -Wfloat-equal -Wwrite-strings -Wendif-labels +LIBS := -lusb +OBJS := sensorapi.o +PROGS := pcsensor pcsensor_sqlite templog + +all: $(PROGS) + +pcsensor: pcsensor.c $(OBJS) + $(CC) $(CFLAGS) -o $@ $^ $(LIBS) + +pcsensor_sqlite: pcsensor_sqlite.c $(OBJS) + $(CC) $(CFLAGS) -o $@ $^ $(LIBS) -lsqlite3 + +templog: templog.c + $(CC) $(CFLAGS) -o $@ $^ -lsqlite3 -lm + +%.o: %.c %.h + $(CC) -c $(CFLAGS) -o $@ $< clean: - rm -f pcsensor *.o + rm -f $(PROGS) $(OBJS) + rules-install: # must be superuser to do this cp ../udev/99-tempsensor.rules /etc/udev/rules.d + diff --git a/src/pcsensor.c b/src/pcsensor.c index 56fb787..857011b 100644 --- a/src/pcsensor.c +++ b/src/pcsensor.c @@ -15,451 +15,188 @@ * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - * THIS SOFTWARE IS PROVIDED BY Philipp Adelt (and other contributors) ''AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Philipp Adelt (or other contributors) BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * THIS SOFTWARE IS PROVIDED BY Philipp Adelt (and other contributors) ''AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Philipp Adelt (or other + * contributors) BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include - +#include #include -#include #include - - -#define VERSION "1.0.0" - -#define VENDOR_ID 0x0c45 -#define PRODUCT_ID 0x7401 - -#define INTERFACE1 0x00 -#define INTERFACE2 0x01 - -const static int reqIntLen=8; -const static int reqBulkLen=8; -const static int endpoint_Int_in=0x82; /* endpoint 0x81 address for IN */ -const static int endpoint_Int_out=0x00; /* endpoint 1 address for OUT */ -const static int endpoint_Bulk_in=0x82; /* endpoint 0x81 address for IN */ -const static int endpoint_Bulk_out=0x00; /* endpoint 1 address for OUT */ -const static int timeout=5000; /* timeout in ms */ - -const static char uTemperatura[] = { 0x01, 0x80, 0x33, 0x01, 0x00, 0x00, 0x00, 0x00 }; -const static char uIni1[] = { 0x01, 0x82, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00 }; -const static char uIni2[] = { 0x01, 0x86, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00 }; - -static int bsalir=1; -static int debug=0; -static int seconds=5; -static int formato=0; -static int mrtg=0; -static int calibration=0; +#include "sensorapi.h" -void bad(const char *why) { - fprintf(stderr,"Fatal error> %s\n",why); - exit(17); -} - - -usb_dev_handle *find_lvr_winusb(); - -void usb_detach(usb_dev_handle *lvr_winusb, int iInterface) { - int ret; - - ret = usb_detach_kernel_driver_np(lvr_winusb, iInterface); - if(ret) { - if(errno == ENODATA) { - if(debug) { - printf("Device already detached\n"); - } - } else { - if(debug) { - printf("Detach failed: %s[%d]\n", - strerror(errno), errno); - printf("Continuing anyway\n"); - } - } - } else { - if(debug) { - printf("detach successful\n"); - } - } -} - -usb_dev_handle* setup_libusb_access(int devicenum) { - usb_dev_handle *lvr_winusb; - - if(debug) { - usb_set_debug(255); - } else { - usb_set_debug(0); - } - usb_init(); - usb_find_busses(); - usb_find_devices(); - - - if(!(lvr_winusb = find_lvr_winusb(devicenum))) { - printf("Couldn't find the USB device, Exiting\n"); - return NULL; - } - - - usb_detach(lvr_winusb, INTERFACE1); - - - usb_detach(lvr_winusb, INTERFACE2); - - - if (usb_set_configuration(lvr_winusb, 0x01) < 0) { - printf("Could not set configuration 1\n"); - return NULL; - } +#define VERSION "1.0.0" +static int bsalir=1; - // Microdia tiene 2 interfaces - if (usb_claim_interface(lvr_winusb, INTERFACE1) < 0) { - printf("Could not claim interface\n"); - return NULL; - } - - if (usb_claim_interface(lvr_winusb, INTERFACE2) < 0) { - printf("Could not claim interface\n"); - return NULL; - } - return lvr_winusb; +void ex_program(int sig) +{ + bsalir=1; + (void) signal(SIGINT, SIG_DFL); } - - - -usb_dev_handle *find_lvr_winusb(int devicenum) { - // iterates to the devicenum'th device for installations with multiple sensors - struct usb_bus *bus; - struct usb_device *dev; - - for (bus = usb_busses; bus; bus = bus->next) { - for (dev = bus->devices; dev; dev = dev->next) { - if (dev->descriptor.idVendor == VENDOR_ID && - dev->descriptor.idProduct == PRODUCT_ID ) { - if (devicenum>0) { - devicenum--; - continue; - } - usb_dev_handle *handle; - if(debug) { - printf("lvr_winusb with Vendor Id: %x and Product Id: %x found.\n", VENDOR_ID, PRODUCT_ID); - } - - if (!(handle = usb_open(dev))) { - printf("Could not open USB device\n"); - return NULL; - } - return handle; - } - } - } - return NULL; -} - - -void ini_control_transfer(usb_dev_handle *dev) { - int r,i; - char question[] = { 0x01,0x01 }; - - r = usb_control_msg(dev, 0x21, 0x09, 0x0201, 0x00, (char *) question, 2, timeout); - if( r < 0 ) - { - perror("USB control write"); bad("USB write failed"); - } - - - if(debug) { - for (i=0;i\n" + " ARGUMENTS:\n" + " OPTIONS:\n" + " -h help\n" + " -v verbose\n" + " -n[i] use device number i (0 is the first one found on the bus)\n" + " -l[n] loop every 'n' seconds, default value is 300\n" + " -c output only in Celsius\n" + " -f output only in Fahrenheit\n" + " -a[n] increase or decrease temperature in 'n' degrees for device calibration\n" + " -m output for mrtg integration\n" + , VERSION + , progname + ); } - -void control_transfer(usb_dev_handle *dev, const char *pquestion) { - int r,i; - char question[reqIntLen]; - - memcpy(question, pquestion, sizeof question); +int main( int argc, char **argv) +{ + usb_temper_t usb_temper; + float tempc; + int c; + struct tm *local; + time_t t; + int devicenum = 0; + int debug=0; + int seconds=5; + int formato=0; + int mrtg=0; + int calibration=0; + + while ((c = getopt (argc, argv, "mfcvhn:l::a:")) != -1) + switch (c) + { + case 'v': + debug = 1; + break; + case 'n': + if (optarg != NULL) { + if (!sscanf(optarg,"%i",&devicenum)==1) { + fprintf (stderr, "Error: '%s' is not numeric.\n", optarg); + exit(EXIT_FAILURE); + } + } + break; + case 'c': + formato=1; //Celsius + break; + case 'f': + formato=2; //Fahrenheit + break; + case 'm': + mrtg=1; + break; + case 'l': + if (optarg!=NULL){ + if (!sscanf(optarg,"%i",&seconds)==1) { + fprintf (stderr, "Error: '%s' is not numeric.\n", optarg); + exit(EXIT_FAILURE); + } else { + bsalir = 0; + break; + } + } else { + bsalir = 0; + seconds = 5; + break; + } + case 'a': + if (!sscanf(optarg,"%i",&calibration)==1) { + fprintf (stderr, "Error: '%s' is not numeric.\n", optarg); + exit(EXIT_FAILURE); + } else { + break; + } + case '?': + case 'h': + usage(argv[0]); + exit(EXIT_FAILURE); + default: + if (isprint (optopt)) + fprintf (stderr, "Unknown option `-%c'.\n", optopt); + else + fprintf (stderr, + "Unknown option character `\\x%x'.\n", + optopt); + exit(EXIT_FAILURE); + } - r = usb_control_msg(dev, 0x21, 0x09, 0x0200, 0x01, (char *) question, reqIntLen, timeout); - if( r < 0 ) + if (optind < argc) { - perror("USB control write"); bad("USB write failed"); + fprintf(stderr, "Non-option ARGV-elements, try -h for help.\n"); + exit(EXIT_FAILURE); } - if(debug) { - for (i=0;itm_hour, + local->tm_min); -} + printf("pcsensor\n"); + } else { + printf("%04d/%02d/%02d %02d:%02d:%02d ", + local->tm_year +1900, + local->tm_mon + 1, + local->tm_mday, + local->tm_hour, + local->tm_min, + local->tm_sec); -void bulk_transfer(usb_dev_handle *dev) { - - int r,i; - char answer[reqBulkLen]; + if (formato==2) { + printf("Temperature %.2fF\n", (9.0 / 5.0 * tempc + 32.0)); + } else if (formato==1) { + printf("Temperature %.2fC\n", tempc); + } else { + printf("Temperature %.2fF %.2fC\n", (9.0 / 5.0 * tempc + 32.0), tempc); + } + } - r = usb_bulk_write(dev, endpoint_Bulk_out, NULL, 0, timeout); - if( r < 0 ) - { - perror("USB bulk write"); bad("USB write failed"); - } - r = usb_bulk_read(dev, endpoint_Bulk_in, answer, reqBulkLen, timeout); - if( r != reqBulkLen ) - { - perror("USB bulk read"); bad("USB read failed"); + if (!bsalir) { sleep(seconds); } } + while (!bsalir); + usb_temper_finish(&usb_temper); - if(debug) { - for (i=0;itm_hour, - local->tm_min); - - printf("pcsensor\n"); - } else { - printf("%04d/%02d/%02d %02d:%02d:%02d ", - local->tm_year +1900, - local->tm_mon + 1, - local->tm_mday, - local->tm_hour, - local->tm_min, - local->tm_sec); - - if (formato==2) { - printf("Temperature %.2fF\n", (9.0 / 5.0 * tempc + 32.0)); - } else if (formato==1) { - printf("Temperature %.2fC\n", tempc); - } else { - printf("Temperature %.2fF %.2fC\n", (9.0 / 5.0 * tempc + 32.0), tempc); - } - } - - if (!bsalir) - sleep(seconds); - } while (!bsalir); - - usb_release_interface(lvr_winusb, INTERFACE1); - usb_release_interface(lvr_winusb, INTERFACE2); - - usb_close(lvr_winusb); - - return 0; -} diff --git a/src/pcsensor_sqlite.c b/src/pcsensor_sqlite.c new file mode 100644 index 0000000..7190d27 --- /dev/null +++ b/src/pcsensor_sqlite.c @@ -0,0 +1,233 @@ +/* + * pcsensor_sqlite.c by Matt Garman (c) 2015 (matthew.garman@gmail.com) + * pcsensor.c by Philipp Adelt (c) 2012 (info@philipp.adelt.net) + * based on Juan Carlos Perez (c) 2011 (cray@isp-sl.com) + * based on Temper.c by Robert Kavaler (c) 2009 (relavak.com) + * All rights reserved. + * + * Temper driver for linux. This program can be compiled either as a library + * or as a standalone program (-DUNIT_TEST). The driver will work with some + * TEMPer usb devices from RDing (www.PCsensor.com). + * + * This driver works with USB devices presenting ID 0c45:7401. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY Philipp Adelt (and other contributors) ''AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Philipp Adelt (or other + * contributors) BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + + +#include +#include +#include +#include +#include +#include + +#include "sensorapi.h" + + +#define BUFLEN (128) +#define DEFAULT_SLEEP_SECS (300) + +#define SQLITE_DB_SCHEMA \ +"CREATE TABLE IF NOT EXISTS temps(\n" \ +" id INTEGER PRIMARY KEY ASC,\n" \ +" timestamp INTEGER NOT NULL, /* unix timestamp from time() call */\n" \ +" tempc REAL NOT NULL /* temperature in celsius */\n" \ +" );\n" + + +char* timestamp() +{ + static char ts_buf[32]; + + time_t t; + time(&t); + struct tm tm; + localtime_r(&t, &tm); + strftime(ts_buf, 32, "%Y%m%d-%H:%M:%S", &tm); + + return ts_buf; +} + +int log_to_sqlite(const char *fn, time_t ts, float tempc) +{ + char sql[BUFLEN]; + int rc = 0; + sqlite3 *db = NULL; + char *err_msg = NULL; + int try = 0; + const int maxtries = 10; + + rc = sqlite3_open(fn, &db); + if (SQLITE_OK != rc) + { + fprintf(stderr, + "[%s] ERROR: sqlite3_open() failure, rc=%d\n", + timestamp(), rc); + return 0; + } + + rc = snprintf(sql, BUFLEN, + "INSERT INTO temps (timestamp,tempc) VALUES (%ld, %lf);", + (long int)ts, tempc); + if (rc >= BUFLEN || rc < 1) + { + fprintf(stderr, + "[%s] ERROR: snprintf() failure, rc=%d\n", + timestamp(), rc); + rc = 0; + goto SQLITE3_CLOSE_LABEL; + } + + do + { + if ( SQLITE_OK == (rc = sqlite3_exec(db, sql, 0, 0, &err_msg)) ) + { + rc = 1; + goto SQLITE3_CLOSE_LABEL; + } + else + { + ++try; + fprintf(stderr, + "[%s] ERROR: sqlite3_exec() failure, " + "try=%i/%i, rc=%d, err_msg=%s\n", + timestamp(), try, maxtries, rc, err_msg); + sqlite3_free(err_msg); + rc = 0; + sleep(1); + } + } + while (try\n" + " ARGUMENTS:\n" + " -f SQLITE_FILE ... path to sqlite3 database file\n" + " OPTIONS:\n" + " -h ............... show this help\n" + " -v ............... verbose/debug mode\n" + " -l SLEEP_SECS .... loop every SLEEP_SECS seconds, default\n" + " is %d\n" + " -a CALIBRATION ... increase or decrease temperature by\n" + " CALIBRATION degrees for device\n" + " calibration\n" + " -S ............... print sqlite3 DB schema and exit\n" + " init DB with:\n" + " %s -S | sqlite3 SQLITE_FILE\n" + , progname + , DEFAULT_SLEEP_SECS + , progname + ); +} + +int main( int argc, char **argv) +{ + usb_temper_t usb_temper; + float tempc; + int c; + struct tm *local; + time_t t; + int devicenum = 0; + int debug=0; + int seconds = DEFAULT_SLEEP_SECS; + int calibration = 0; + char *sqlite_file = NULL; + + while ((c = getopt (argc, argv, "hvSf:l:a:")) != -1) + { + switch (c) + { + case 'v': debug = 1; break; + case 'l': seconds = atoi(optarg); LOOP = 0; break; + case 'a': calibration = atoi(optarg); break; + case 'f': sqlite_file = optarg; break; + case 'S': printf(SQLITE_DB_SCHEMA "\n"); exit(0); break; + case '?': + case 'h': usage(argv[0]); exit(0); + default: + fprintf(stderr, + "ERROR: unknown option %c, use -h for help\n", c); + exit(EXIT_FAILURE); + } + } + + if (optind < argc) + { + fprintf(stderr, "Non-option ARGV-elements, try -h for help.\n"); + exit(EXIT_FAILURE); + } + + usb_temper = usb_temper_init(devicenum, debug, calibration); + + (void) signal(SIGINT, signal_handler); + + + do { + tempc = usb_temper_get_tempc(usb_temper); + + t = time(NULL); + local = localtime(&t); + + if (sqlite_file) + { + if (!log_to_sqlite(sqlite_file, t, tempc)) + { + LOOP = 1; + } + } + else + { + printf("%04d/%02d/%02d %02d:%02d:%02d ", + local->tm_year +1900, + local->tm_mon + 1, + local->tm_mday, + local->tm_hour, + local->tm_min, + local->tm_sec); + + printf("Temperature %.2fF %.2fC\n", + DEG_C_TO_DEG_F(tempc), tempc); + } + + if (!LOOP) { sleep(seconds); } + } + while (!LOOP); + + usb_temper_finish(&usb_temper); + + return 0; +} + diff --git a/src/sensorapi.c b/src/sensorapi.c new file mode 100644 index 0000000..0aad20f --- /dev/null +++ b/src/sensorapi.c @@ -0,0 +1,369 @@ +/* + * pcsensor.c by Philipp Adelt (c) 2012 (info@philipp.adelt.net) + * based on Juan Carlos Perez (c) 2011 (cray@isp-sl.com) + * based on Temper.c by Robert Kavaler (c) 2009 (relavak.com) + * All rights reserved. + * + * Temper driver for linux. This program can be compiled either as a library + * or as a standalone program (-DUNIT_TEST). The driver will work with some + * TEMPer usb devices from RDing (www.PCsensor.com). + * + * This driver works with USB devices presenting ID 0c45:7401. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY Philipp Adelt (and other contributors) ''AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Philipp Adelt (or other + * contributors) BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "sensorapi.h" + +#include +#include +#include + + +#define USB_TEMPER_INTERFACE1 (0x00) +#define USB_TEMPER_INTERFACE2 (0x01) +#define USB_TEMPER_VENDOR_ID (0x0c45) +#define USB_TEMPER_PRODUCT_ID (0x7401) + + +static const char uTemperatura[] = { 0x01, 0x80, 0x33, 0x01, 0x00, 0x00, 0x00, 0x00 }; +static const char uIni1[] = { 0x01, 0x82, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00 }; +static const char uIni2[] = { 0x01, 0x86, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00 }; + + +const static int reqIntLen=8; +const static int reqBulkLen=8; +const static int endpoint_Int_in=0x82; /* endpoint 0x81 address for IN */ +const static int endpoint_Int_out=0x00; /* endpoint 1 address for OUT */ +const static int endpoint_Bulk_in=0x82; /* endpoint 0x81 address for IN */ +const static int endpoint_Bulk_out=0x00; /* endpoint 1 address for OUT */ +const static int timeout=5000; /* timeout in ms */ + +static int DEBUG=0; +static int CALIBRATION=0; + + +void bad(const char *why) +{ + fprintf(stderr, "Fatal error> %s\n", why); + exit(17); +} + +usb_dev_handle *find_lvr_winusb(); + +void usb_detach(usb_dev_handle *lvr_winusb, int iInterface) +{ + int ret; + ret = usb_detach_kernel_driver_np(lvr_winusb, iInterface); + if(ret) { + if(errno == ENODATA) { + if(DEBUG) { + printf("Device already detached\n"); + } + } else { + if(DEBUG) { + printf("Detach failed: %s[%d]\n", + strerror(errno), errno); + printf("Continuing anyway\n"); + } + } + } else { + if(DEBUG) { + printf("detach successful\n"); + } + } +} + +usb_dev_handle* setup_libusb_access(int devicenum) +{ + usb_dev_handle *lvr_winusb; + + if(DEBUG) { + usb_set_debug(255); + } else { + usb_set_debug(0); + } + usb_init(); + usb_find_busses(); + usb_find_devices(); + + + if(!(lvr_winusb = find_lvr_winusb(devicenum))) { + printf("Couldn't find the USB device, Exiting\n"); + return NULL; + } + + + usb_detach(lvr_winusb, USB_TEMPER_INTERFACE1); + + usb_detach(lvr_winusb, USB_TEMPER_INTERFACE2); + + if (usb_set_configuration(lvr_winusb, 0x01) < 0) { + printf("Could not set configuration 1\n"); + return NULL; + } + + + // Microdia tiene 2 interfaces + if (usb_claim_interface(lvr_winusb, USB_TEMPER_INTERFACE1) < 0) { + printf("Could not claim interface\n"); + return NULL; + } + + if (usb_claim_interface(lvr_winusb, USB_TEMPER_INTERFACE2) < 0) { + printf("Could not claim interface\n"); + return NULL; + } + + return lvr_winusb; +} + + +usb_dev_handle *find_lvr_winusb(int devicenum) +{ + // iterates to the devicenum'th device for installations with multiple sensors + struct usb_bus *bus; + struct usb_device *dev; + + for (bus = usb_busses; bus; bus = bus->next) { + for (dev = bus->devices; dev; dev = dev->next) { + if (dev->descriptor.idVendor == USB_TEMPER_VENDOR_ID && + dev->descriptor.idProduct == USB_TEMPER_PRODUCT_ID ) { + if (devicenum>0) { + devicenum--; + continue; + } + usb_dev_handle *handle; + if(DEBUG) { + printf("lvr_winusb with Vendor Id: %x and Product Id: %x found.\n", USB_TEMPER_VENDOR_ID, USB_TEMPER_PRODUCT_ID); + } + + if (!(handle = usb_open(dev))) { + printf("Could not open USB device\n"); + return NULL; + } + return handle; + } + } + } + return NULL; +} + +void ini_control_transfer(usb_dev_handle *dev) +{ + int r,i; + + char question[] = { 0x01,0x01 }; + + r = usb_control_msg(dev, 0x21, 0x09, 0x0201, 0x00, (char *) question, 2, timeout); + if ( r < 0 ) + { + perror("USB control write"); bad("USB write failed"); + } + + if (DEBUG) { + for (i=0; i<(sizeof(question)/sizeof(question[0])); i++) { + printf("%02x ",question[i] & 0xFF); + } + printf("\n"); + } +} + +void control_transfer(usb_dev_handle *dev, const char *pquestion) +{ + int r,i; + + char question[reqIntLen]; + + memcpy(question, pquestion, sizeof question); + + r = usb_control_msg(dev, 0x21, 0x09, 0x0200, 0x01, (char *) question, reqIntLen, timeout); + if( r < 0 ) + { + perror("USB control write"); bad("USB write failed"); + } + + if(DEBUG) { + for (i=0;iusb_handle = usb_handle; + usb_temper->devicenum = devicenum; + + DEBUG = debug; + CALIBRATION = calibration; + + return usb_temper; +} + +float usb_temper_get_tempc(usb_temper_t usb_temper) +{ + // return an impossible temperature (absolute zero = -273.15) if bad + // inputs or some other failure + float tempc = -9999.99; + + if (usb_temper && usb_temper->usb_handle) + { + control_transfer(usb_temper->usb_handle, uTemperatura ); + interrupt_read_temperatura(usb_temper->usb_handle, &tempc); + } + + return tempc; +} + +int usb_temper_finish(usb_temper_t* usb_temper) +{ + if (usb_temper && (*usb_temper) && (*usb_temper)->usb_handle) + { + usb_release_interface((*usb_temper)->usb_handle, USB_TEMPER_INTERFACE1); + usb_release_interface((*usb_temper)->usb_handle, USB_TEMPER_INTERFACE2); + usb_close((*usb_temper)->usb_handle); + free(*usb_temper); + *usb_temper = NULL; + } + + return 0; +} + diff --git a/src/sensorapi.h b/src/sensorapi.h new file mode 100644 index 0000000..ed6bfbc --- /dev/null +++ b/src/sensorapi.h @@ -0,0 +1,56 @@ +#ifndef SENSOR_API_H__ +#define SENSOR_API_H__ + +/* + * pcsensor.c by Philipp Adelt (c) 2012 (info@philipp.adelt.net) + * based on Juan Carlos Perez (c) 2011 (cray@isp-sl.com) + * based on Temper.c by Robert Kavaler (c) 2009 (relavak.com) + * All rights reserved. + * + * Temper driver for linux. This program can be compiled either as a library + * or as a standalone program (-DUNIT_TEST). The driver will work with some + * TEMPer usb devices from RDing (www.PCsensor.com). + * + * This driver works with USB devices presenting ID 0c45:7401. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY Philipp Adelt (and other contributors) ''AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Philipp Adelt (or other + * contributors) BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#define MALLOC(n) malloc_wrapper((n), __FILE__, __LINE__) + +#define DEG_C_TO_DEG_F(deg_c) ((9.0 / 5.0 * (deg_c) + 32.0)) + +struct usb_temper +{ + usb_dev_handle* usb_handle; + int devicenum; + int debug; +}; + +typedef struct usb_temper* usb_temper_t; + +usb_temper_t usb_temper_init(int devicenum, int debug, int calibration); +float usb_temper_get_tempc(usb_temper_t usb_temper); +int usb_temper_finish(usb_temper_t* usb_temper); + +void* malloc_wrapper(size_t nbytes, const char* file, int line); + +#endif // SENSOR_API_H__ + diff --git a/src/templog.c b/src/templog.c new file mode 100644 index 0000000..5714228 --- /dev/null +++ b/src/templog.c @@ -0,0 +1,324 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFLEN (128) +#define DEFAULT_N_RECORDS (10) +#define DEG_C_TO_DEG_F(deg_c) ((9.0 / 5.0 * (deg_c) + 32.0)) + +typedef struct temp_db_data +{ + time_t ts; + double tempc; +} +temp_db_data_t; + +typedef struct stats +{ + size_t n; + double min; + time_t min_time; + double max; + time_t max_time; + double med; + double avg; + double std; +} +stats_t; + +int double_compare(const void* d1, const void* d2) +{ + return ( *((double*)d1) < *((double*)d2) ); +} + +int double_is_zero(double d) +{ + const static double double_zero = 0.0; + return (0 == memcmp(&d, &double_zero, sizeof(double_zero))); +} + +int calc_temp_stats(const temp_db_data_t *db_data, stats_t *stats) +{ + if (NULL == db_data || NULL == stats) { return 0; } + + stats->n = 0; + stats->min = 999999.9; + stats->min_time = 0; + stats->max = 0.0; + stats->max_time = 0; + stats->med = 0; + stats->avg = 0; + stats->std = 0; + + for ( stats->n=0; + ( 0!=db_data[stats->n].ts && + !double_is_zero(db_data[stats->n].tempc) ); + ++stats->n) + { + double tempc = db_data[stats->n].tempc; // convenience variable + time_t ts = db_data[stats->n].ts; // convenience variable + stats->avg += tempc; + + if (tempc > stats->max) { stats->max=tempc; stats->max_time=ts; } + if (tempc < stats->min) { stats->min=tempc; stats->min_time=ts; } + } + + stats->avg /= stats->n; + + double *sorted = malloc(stats->n * sizeof(double)); + + size_t i; + for (i=0; in; ++i) + { + double tempc = db_data[i].tempc; // convenience variable + sorted[i] = tempc; + stats->std += ((tempc - stats->avg) * (tempc - stats->avg)); + } + + stats->std /= stats->n; + stats->std = sqrt(stats->std); + + qsort(sorted, stats->n, sizeof(double), double_compare); + + //printf("DEBUG: sorted: "); + //for (i=0; in; ++i) + //{ + // printf("%.1lf, ", sorted[i]); + //} + //printf("\n"); + + stats->med = sorted[stats->n/2]; + + free(sorted); + + return 1; +} + +int print_stats(FILE* fp, const stats_t *stats) +{ + if (NULL == stats) { return 0; } + if (NULL == fp) { fp = stdout; } + + char min_time_str[BUFLEN]; + char max_time_str[BUFLEN]; + + struct tm *min_tm = localtime(&stats->min_time); + strftime(min_time_str, BUFLEN, "%c", min_tm); + + struct tm *max_tm = localtime(&stats->max_time); + strftime(max_time_str, BUFLEN, "%c", max_tm); + + fprintf(fp, + "STATS:\n" + " n ..... %zu\n" + " min ... %5.1lf C, %5.1lf F @ %s\n" + " max ... %5.1lf C, %5.1lf F @ %s\n" + " med ... %5.1lf C, %5.1lf F\n" + " avg ... %5.1lf C, %5.1lf F\n" + " std ... %5.1lf C, %5.1lf F\n" + , stats->n + , stats->min, DEG_C_TO_DEG_F(stats->min), min_time_str + , stats->max, DEG_C_TO_DEG_F(stats->max), max_time_str + , stats->med, DEG_C_TO_DEG_F(stats->med) + , stats->avg, DEG_C_TO_DEG_F(stats->avg) + , stats->std, DEG_C_TO_DEG_F(stats->std) + ); + + return 1; +} + +int print_sqlite_result(FILE* fp, const temp_db_data_t *db_data) +{ + if (NULL == fp) { fp = stdout; } + + if (NULL == db_data) { return 0; } + + size_t i = 0; + + for (i=0; (0!=db_data[i].ts && !double_is_zero(db_data[i].tempc)); ++i) + { + char buf[BUFLEN]; + struct tm *tm = localtime(&db_data[i].ts); + double tempf = (9.0 / 5.0 * db_data[i].tempc + 32.0); + strftime(buf, BUFLEN, "%c", tm); + + fprintf(fp, "%s [%ld]: %.1lf deg F (%.1lf deg C)\n" + , buf + , (long int)db_data[i].ts + , tempf + , db_data[i].tempc); + } + + return 1; +} + +temp_db_data_t* fetch_from_sqlite(const char *fn, int n, int secs) +{ + const char *sql = + "SELECT timestamp,tempc FROM temps ORDER BY timestamp;" ; + int rc = 0; + sqlite3 *db = NULL; + sqlite3_stmt *stmt = NULL; + time_t now; + + if (n>0) + { + sql = "SELECT timestamp,tempc FROM temps ORDER BY timestamp DESC LIMIT ?;" ; + } + else if (secs>0) + { + sql = "SELECT timestamp,tempc FROM temps WHERE timestamp > ? ORDER BY timestamp;" ; + } + + rc = sqlite3_open(fn, &db); + if (SQLITE_OK != rc) + { + fprintf(stderr, "ERROR: sqlite3_open() failure, rc=%d\n", rc); + return NULL; + } + + rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0); + if (rc == SQLITE_OK) + { + if (n>0) + { + sqlite3_bind_int(stmt, 1, n); + } + else if (secs>0) + { + time(&now); + now -= secs; + sqlite3_bind_int(stmt, 1, secs); + } + } + else + { + fprintf(stderr, "ERROR: sqlite3_prepare_v2(): %s\n", + sqlite3_errmsg(db)); + return NULL; + } + + size_t db_data_size = 256; + temp_db_data_t *db_data = NULL; + db_data = malloc(db_data_size*sizeof(temp_db_data_t)); + + size_t n_rows = 0; + int step; + while ( SQLITE_ROW == (step = sqlite3_step(stmt)) ) + { + if (n_rows == (db_data_size-2)) + { + db_data_size *= 2; + db_data = realloc(db_data, (db_data_size*sizeof(temp_db_data_t))); + if (NULL == db_data) + { + fprintf(stderr, "ERROR: realloc() failure\n"); + exit(-1); + } + } + time_t temp_i = sqlite3_column_int(stmt, 0); + double temp_d = sqlite3_column_double(stmt, 1); + db_data[n_rows].ts = temp_i; + db_data[n_rows].tempc = temp_d; + ++n_rows; + } + db_data[n_rows].ts = db_data[n_rows].tempc = 0; + + sqlite3_finalize(stmt); + sqlite3_close(db); + + return db_data; +} + +void usage(const char* progname) +{ + fprintf(stderr, + "Usage: %s [options] \n" + " ARGUMENTS:\n" + " -f SQLITE_FILE ... query SQLITE_FILE for temp logs\n" + " OPTIONS:\n" + " -n N_RECORDS ..... query N_RECORDS records from DB, default=%d\n" + " -H HOURS ......... query last HOURS worth of records\n" + " -d DAYS .......... query last DAYS worth of records\n" + " days takes precedence over hours\n" + " set -n 0 to use days or hours\n" + " -s STYLE ......... print style, bit mask:\n" + " 1 = individual records\n" + " 2 = summary stats\n" + " 3 = both records and stats\n" + " -h ............... show this help\n" + , progname + , DEFAULT_N_RECORDS + ); +} + +int main(int argc, char **argv) +{ + enum + { + STYLE_RECORDS=1, + STYLE_STATS=2, + }; + + int c; + char *sqlite_file = NULL; + struct stat statbuf; + int n_records = DEFAULT_N_RECORDS; + int hours = 0; + int days = 0; + int secs = 0; + int style = 0; + + while ( -1 != (c = getopt(argc, argv, "?hf:n:H:d:s:")) ) + { + switch (c) + { + case 'f': sqlite_file = optarg; break; + case 'n': n_records = atoi(optarg); break; + case 'H': hours = atoi(optarg); break; + case 'd': days = atoi(optarg); break; + case 's': style = atoi(optarg); break; + case '?': + case 'h': + default: usage(argv[0]); exit(0); break; + } + } + + if (NULL == sqlite_file) + { + fprintf(stderr, "ERROR: sqlite_file not provided\n"); + exit(-1); + } + else if (0 != stat(sqlite_file, &statbuf)) + { + fprintf(stderr, "ERROR: stat() failure for \"%s\"\n", sqlite_file); + exit(-1); + } + + if (hours > 0) { secs = 60*60; } + if (days > 0) { secs = 60*60*24; } + + temp_db_data_t* db_data = fetch_from_sqlite(sqlite_file, n_records, secs); + if (style & STYLE_STATS) + { + stats_t stats; + calc_temp_stats(db_data, &stats); + print_stats(stdout, &stats); + } + + if (style & STYLE_RECORDS) + { + print_sqlite_result(stdout, db_data); + } + + free(db_data); + + return 0; +} +