2014年8月12日火曜日

USB温度計

昨年末辺りからUSB接続の温度計でPC付近の室温を計測しています。
上海問屋で買ったものですが、PCsenserのTEMPerと同じ物のようです。

CentOS 6でpcsensor-1.0.0-multi.tgzを少し弄って使用しています。

MRTG用の出力オプションを使用して利用するつもりでしたが、私の環境では1回起動すると2回目以降正常動作しませんでした。
機器を抜き差しすると1回は正常動作するため、初期化か終了処理がうまくいっていないようなのですが、いろいろ試しても直せませんでした。

取りあえずループモード(-lオプション)では動作するので、次のようにHTTPで情報取得できるようにして使用しています。
$ curl http://localhost:9999/
30.12
30.12
10:37
pcsensor
$

pcsenser-1.0.0-multi との差分は下記になります。
Index: Makefile
===================================================================
--- Makefile    (.../vendor/pcsensor/1.0.0-multi)
+++ Makefile    (.../pcsensor/trunk)
@@ -3,7 +3,7 @@
 CFLAGS = -O2 -Wall

 pcsensor:      pcsensor.c
-       ${CC} -DUNIT_TEST -o $@ $^ -lusb
+       ${CC} -DUNIT_TEST -o $@ $^ -lusb -levent

 clean:
        rm -f pcsensor *.o
Index: pcsensor.c
===================================================================
--- pcsensor.c  (.../vendor/pcsensor/1.0.0-multi)
+++ pcsensor.c  (.../pcsensor/trunk)
@@ -34,6 +34,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 


 #define VERSION "1.0.0"
@@ -45,6 +47,8 @@
 #define INTERFACE2 0x01

 #define MAX_DEV 4
+#define PID_FILE "/var/run/pcsensor.pid"
+#define PORT_NUM 9999

 const static int reqIntLen=8;
 const static int reqBulkLen=8;
@@ -64,6 +68,7 @@
 static int formato=0;
 static int mrtg=0;
 static int calibration=0;
+static int use_daemon=0;

 static usb_dev_handle *handles[MAX_DEV];

@@ -78,24 +83,24 @@
 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");
-               }
-       }
+        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");
+                }
+        }
 }

 int setup_libusb_access() {
@@ -152,10 +157,10 @@

      struct usb_bus *bus;
         struct usb_device *dev;
-       int i;
+        int i;

-       memset(handles, 0, sizeof(handles));
-       i = 0;
+        memset(handles, 0, sizeof(handles));
+        i = 0;
         for (bus = usb_busses; bus; bus = bus->next) {
         for (dev = bus->devices; dev; dev = dev->next) {
                         if (dev->descriptor.idVendor == VENDOR_ID &&
@@ -170,8 +175,8 @@
                                         continue;
                                 }
                                 handles[i++] = handle;
-                               if (i == MAX_DEV)
-                                       break;
+                                if (i == MAX_DEV)
+                                        break;
                         }
                 }
         }
@@ -314,6 +319,81 @@

       (void) signal(SIGINT, SIG_DFL);
 }
+
+void daemonize()
+{
+     pid_t pid;
+
+     pid = fork();
+     if (pid < 0) {
+        perror("failed fork()");
+        exit(1);
+     }
+
+     if (pid > 0) {
+        FILE *fp;
+        fp = fopen(PID_FILE, "w");
+        if (fp != NULL) {
+          fprintf(fp, "%d\n", pid);
+          fclose(fp);
+        }
+        exit(0);
+     }
+     seteuid(99);
+     setsid();
+}
+
+void http_handler(struct evhttp_request *req, void *arg)
+{
+        int i;
+        char msg[1024];
+
+        if (req->type != EVHTTP_REQ_GET) {
+                evhttp_send_error(req, HTTP_SERVUNAVAIL, "server unavail");
+                return;
+        }
+
+        struct evbuffer *ebuf;
+        ebuf = evbuffer_new();
+        if (ebuf == NULL) {
+                evhttp_send_error(req, HTTP_SERVUNAVAIL, "Cannot allocate buffer");
+                return;
+        }
+
+        for (i = 0; handles[i] != NULL && i < MAX_DEV; i++) {
+                float tempc;
+                struct tm *local;
+                time_t t;
+
+                memset(msg, 0, sizeof(msg));
+                control_transfer(handles[i], uTemperatura );
+                interrupt_read_temperatura(handles[i], &tempc);
+
+                t = time(NULL);
+                local = localtime(&t);
+
+                if (formato==2) {
+                        tempc = (9.0 / 5.0 * tempc + 32.0);
+                }
+                snprintf(msg, sizeof(msg), "%.2f\n%.2f\n%02d:%02d\npcsensor\n", tempc, tempc, local->tm_hour, local->tm_min);
+        }
+        char content_length[8];
+
+        snprintf(content_length, sizeof(content_length)-1, "%d", strlen(msg));
+
+        evhttp_add_header(req->output_headers, "Content-Type", "text/plain");
+        evhttp_add_header(req->output_headers, "Content-Length", content_length);
+        evbuffer_add(ebuf, msg, strlen(msg));
+        evhttp_send_reply(req, HTTP_OK, "", ebuf);
+        evbuffer_free(ebuf);
+}
+
+void signal_cb(int fd, short event, void *arg)
+{
+        struct event *signal = arg;
+        // printf("%s: got signal %d, ev=%0x\n", __func__, EVENT_SIGNAL(signal), event);
+        event_base_loopexit(signal->ev_base, 0);
+}

 int main( int argc, char **argv) {

@@ -323,7 +403,7 @@
      time_t t;

      memset(handles, 0, sizeof(handles));
-     while ((c = getopt (argc, argv, "mfcvhl::a:")) != -1)
+     while ((c = getopt (argc, argv, "mfcvhl::a:d")) != -1)
      switch (c)
        {
        case 'v':
@@ -359,19 +439,23 @@
          } else {
               break;
          }
+       case 'd':
+         use_daemon=1;
+         break;
        case '?':
        case 'h':
          printf("pcsensor version %s\n",VERSION);
-        printf("      Aviable options:\n");
-        printf("          -h help\n");
-        printf("          -v verbose\n");
-        printf("          -l[n] loop every 'n' seconds, default value is 5s\n");
-        printf("          -c output only in Celsius\n");
-        printf("          -f output only in Fahrenheit\n");
-        printf("          -a[n] increase or decrease temperature in 'n' degrees for device calibration\n");
-        printf("          -m output for mrtg integration\n");
+         printf("      Aviable options:\n");
+         printf("          -h help\n");
+         printf("          -v verbose\n");
+         printf("          -l[n] loop every 'n' seconds, default value is 5s\n");
+         printf("          -c output only in Celsius\n");
+         printf("          -f output only in Fahrenheit\n");
+         printf("          -a[n] increase or decrease temperature in 'n' degrees for device calibration\n");
+         printf("          -m output for mrtg integration\n");
+         printf("          -d daemon for mrtg integration\n");

-        exit(EXIT_FAILURE);
+         exit(EXIT_FAILURE);
        default:
          if (isprint (optopt))
            fprintf (stderr, "Unknown option `-%c'.\n", optopt);
@@ -394,67 +478,112 @@
      (void) signal(SIGINT, ex_program);

      for (i = 0; handles[i] != NULL && i < MAX_DEV; i++) {
-            ini_control_transfer(handles[i]);
+             ini_control_transfer(handles[i]);

-            control_transfer(handles[i], uTemperatura );
-            interrupt_read(handles[i]);
+             control_transfer(handles[i], uTemperatura );
+             interrupt_read(handles[i]);

-            control_transfer(handles[i], uIni1 );
-            interrupt_read(handles[i]);
+             control_transfer(handles[i], uIni1 );
+             interrupt_read(handles[i]);

-            control_transfer(handles[i], uIni2 );
-            interrupt_read(handles[i]);
-            interrupt_read(handles[i]);
-       }
+             control_transfer(handles[i], uIni2 );
+             interrupt_read(handles[i]);
+             interrupt_read(handles[i]);
+        }

-     do {
-       for (i = 0; handles[i] != NULL && i < MAX_DEV; i++) {
-           control_transfer(handles[i], uTemperatura );
-           interrupt_read_temperatura(handles[i], &tempc);
+     if (use_daemon) {
+        struct event_base *ebase;
+        struct evhttp *http;
+        struct event signal[2];

-           t = time(NULL);
-           local = localtime(&t);
+        daemonize();

-           if (mrtg) {
-              if (formato==2) {
-                  printf("%.2f\n", (9.0 / 5.0 * tempc + 32.0));
-                  printf("%.2f\n", (9.0 / 5.0 * tempc + 32.0));
-              } else {
-                  printf("%.2f\n", tempc);
-                  printf("%.2f\n", tempc);
-              }
-
-              printf("%02d:%02d\n",
-                          local->tm_hour,
-                          local->tm_min);
+        ebase = event_base_new();
+        if (ebase == NULL) {
+                perror("failed event_base_new()");
+                goto EXIT;
+        }
+        signal_set(&signal[0], SIGHUP, signal_cb, &signal[0]);
+        event_base_set(ebase, &signal[0]);
+        signal_add(&signal[0], 0);

-              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);
+        signal_set(&signal[1], SIGTERM, signal_cb, &signal[1]);
+        event_base_set(ebase, &signal[1]);
+        signal_add(&signal[1], 0);

-              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);
+        http = evhttp_new(ebase);
+        if (http == NULL) {
+                perror("failed evhttp_new()");
+                event_base_free(ebase);
+                goto EXIT;
+        }
+
+        if (evhttp_bind_socket(http, "127.0.0.1", PORT_NUM) < 0) {
+                perror("failed evhttp_bind_socket()");
+                evhttp_free(http);
+                event_base_free(ebase);
+                goto EXIT;
+        }
+
+        evhttp_set_gencb(http, http_handler, NULL);
+        event_base_dispatch(ebase);
+
+        evhttp_free(http);
+        event_base_free(ebase);
+        unlink(PID_FILE);
+     }
+     else {
+             do {
+                for (i = 0; handles[i] != NULL && i < MAX_DEV; i++) {
+                   control_transfer(handles[i], uTemperatura );
+                   interrupt_read_temperatura(handles[i], &tempc);
+
+                   t = time(NULL);
+                   local = localtime(&t);
+
+                   if (mrtg) {
+                      if (formato==2) {
+                          printf("%.2f\n", (9.0 / 5.0 * tempc + 32.0));
+                          printf("%.2f\n", (9.0 / 5.0 * tempc + 32.0));
+                      } else {
+                          printf("%.2f\n", tempc);
+                          printf("%.2f\n", tempc);
+                      }
+
+                      printf("%02d:%02d\n",
+                                  local->tm_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);
+    }

+EXIT:
     for (i = 0; handles[i] != NULL && i < MAX_DEV; i++) {
      usb_release_interface(handles[i], INTERFACE1);
      usb_release_interface(handles[i], INTERFACE2);
+     usb_reset(handles[i]);

      usb_close(handles[i]);
     }
USB温度計を複数持っていないので試していませんが、恐らく複数機器ではうまく動かないかと…。
初めてlibeventを使ってみましたが、面白いですね。