now actually works
This commit is contained in:
parent
26c3dda8ea
commit
b43f8e027a
10
Makefile
10
Makefile
|
@ -1,10 +1,12 @@
|
||||||
CFLAGS=-g -Wall `pkg-config --cflags hidapi`
|
CFLAGS=-c -D_LINUX -g -Wall -I/usr/include/hidapi -I/usr/include/libusb-1.0
|
||||||
LDFLAGS=`pkg-config --libs hidapi`
|
LDFLAGS=-L/usr/lib/x86_64-linux-gnu/ -lhidapi-hidraw -lusb-1.0
|
||||||
|
|
||||||
all: lsusb howard
|
all: lsusb howard
|
||||||
|
|
||||||
lsusb: lsusb.c
|
lsusb: lsusb.c
|
||||||
gcc -olsusb $(CFLAGS) $(LDFLAGS) lsusb.c
|
gcc $(CFLAGS) lsusb.c -olsusb.o
|
||||||
|
gcc lsusb.o $(LDFLAGS) -o lsusb
|
||||||
|
|
||||||
howard: howard.c howard.h
|
howard: howard.c howard.h
|
||||||
gcc -ohoward $(CFLAGS) $(LDFLAGS) howard.c
|
gcc $(CFLAGS) howard.c -ohoward.o
|
||||||
|
gcc howard.o $(LDFLAGS) -o howard
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
# Overview
|
# Overview
|
||||||
|
|
||||||
|
|
||||||
Sound level monitoring via USB Benetech GM1356.
|
Sound level monitoring via USB Benetech GM1356.
|
||||||
|
|
||||||
Named after [Loud Howard](https://dilbert.com/strip/1995-04-21).
|
Named after [Loud Howard](https://dilbert.com/strip/1995-04-21).
|
||||||
|
|
||||||
Requirements:
|
Big thanks to [Maciej Ciemborowicz](https://github.com/ciembor) for his work documenting the [USB protocol](https://github.com/dobra-noc/gm1356/blob/master/PROTOCOL.md) this device uses.
|
||||||
|
|
||||||
|
# Requirements
|
||||||
- [hidapi](https://github.com/libusb/hidapi)
|
- [hidapi](https://github.com/libusb/hidapi)
|
||||||
- An OS which recognises the GM1356 as a USB device (ie. not OSX Big Sur).
|
- An OS which recognises the GM1356 as a USB device (ie. not OSX Big Sur).
|
||||||
|
|
||||||
|
@ -17,4 +18,6 @@ TBC
|
||||||
|
|
||||||
TBC
|
TBC
|
||||||
|
|
||||||
|
# Implementation Notes
|
||||||
|
|
||||||
|
- The device seems to 'hang' periodically and cause all subsequent reads to timeout. To work around this, after two concurrent timeouts I send an `IOCTL_USBDEVFS_RESET`.
|
||||||
|
|
425
howard.c
425
howard.c
|
@ -1,6 +1,11 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifdef _LINUX
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _TERMUX
|
#ifdef _TERMUX
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
@ -14,10 +19,16 @@
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
#include <sys/errno.h>
|
#include <sys/errno.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <sys/time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <linux/usbdevice_fs.h>
|
||||||
|
#include <libusb.h>
|
||||||
#include "howard.h"
|
#include "howard.h"
|
||||||
|
|
||||||
#define TRUE (-1)
|
#define TRUE (-1)
|
||||||
|
@ -29,11 +40,12 @@
|
||||||
// Benestar sound meter
|
// Benestar sound meter
|
||||||
//#define MIC_VID (0x1630)
|
//#define MIC_VID (0x1630)
|
||||||
//#define MIC_PID (0x05dc)
|
//#define MIC_PID (0x05dc)
|
||||||
#define MIC_VID (0x8817)
|
#define MIC_VID "64bd"
|
||||||
#define MIC_PID (0x2109)
|
#define MIC_PID "74e3"
|
||||||
|
|
||||||
#define CMD_CAPTURE (0xb3)
|
#define CMD_CAPTURE (0xb3)
|
||||||
|
|
||||||
|
hid_device *dev = NULL;
|
||||||
|
|
||||||
const char *rangestr[] = {
|
const char *rangestr[] = {
|
||||||
"30-130",
|
"30-130",
|
||||||
|
@ -43,10 +55,18 @@ const char *rangestr[] = {
|
||||||
"80-130",
|
"80-130",
|
||||||
};
|
};
|
||||||
|
|
||||||
char influxhost[BUFLENSMALL];
|
char *influxhost = NULL;
|
||||||
char influxdb[BUFLENSMALL];
|
char *influxdb = NULL;
|
||||||
char influxuser[BUFLENSMALL];
|
char *influxuser = NULL;
|
||||||
char influxpass[BUFLENSMALL];
|
char *influxpass = NULL;
|
||||||
|
int doit = FALSE;
|
||||||
|
int verbose = FALSE;
|
||||||
|
|
||||||
|
int outmode = 0;
|
||||||
|
|
||||||
|
#define OM_SHORT (1)
|
||||||
|
#define OM_LONG (2)
|
||||||
|
#define OM_DB (4)
|
||||||
|
|
||||||
#define FLAG_DBCMODE (0x10)
|
#define FLAG_DBCMODE (0x10)
|
||||||
#define FLAG_FASTMODE (0x40)
|
#define FLAG_FASTMODE (0x40)
|
||||||
|
@ -74,6 +94,67 @@ void colprintf( char *prefix, const char *col, char *format, va_list *args ) {
|
||||||
printf("%s\n",PLAIN);
|
printf("%s\n",PLAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *getdevpath(int vid, int pid, char *retvar) {
|
||||||
|
struct libusb_device **devlist;
|
||||||
|
struct libusb_context *ctx;
|
||||||
|
int i,ndevices,found = 0;
|
||||||
|
|
||||||
|
if (libusb_init(&ctx)) {
|
||||||
|
err("libusb_init() failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ndevices = libusb_get_device_list(ctx, &devlist);
|
||||||
|
for (i = 0; i < ndevices; i++) {
|
||||||
|
struct libusb_device* thisdev = devlist[i];
|
||||||
|
struct libusb_device_descriptor desc;
|
||||||
|
libusb_get_device_descriptor(thisdev, &desc);
|
||||||
|
if (desc.idVendor == vid && desc.idProduct == pid) {
|
||||||
|
int busnum = -1,devnum = -1;
|
||||||
|
busnum = libusb_get_bus_number(thisdev);
|
||||||
|
devnum = libusb_get_device_address(thisdev);
|
||||||
|
snprintf(retvar, BUFLEN, "/dev/bus/usb/%03d/%03d",busnum,devnum);
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
libusb_free_device_list(devlist, 1);
|
||||||
|
libusb_exit(ctx);
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
return retvar;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int resetusb(char *filename) {
|
||||||
|
int fd,rv = 0;
|
||||||
|
strncpy(filename, "/dev/bus/usb/003/006", BUFLEN);
|
||||||
|
|
||||||
|
warn("Attempting to reset usb port");
|
||||||
|
fd = open(filename, O_WRONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
err("Failed to open %s, details:", filename);
|
||||||
|
perror("open()");
|
||||||
|
rv = 1;
|
||||||
|
} else {
|
||||||
|
int rc;
|
||||||
|
rc = ioctl(fd, USBDEVFS_RESET, 0);
|
||||||
|
if (rc < 0) {
|
||||||
|
err("Failed to send reset, details:", filename);
|
||||||
|
perror("ioctl()");
|
||||||
|
rv = 1;
|
||||||
|
} else {
|
||||||
|
info("Reset successful");
|
||||||
|
rv = 0;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void info( char* format, ... ) {
|
void info( char* format, ... ) {
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
|
@ -81,6 +162,14 @@ void info( char* format, ... ) {
|
||||||
va_end( args );
|
va_end( args );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void vinfo( char* format, ... ) {
|
||||||
|
va_list args;
|
||||||
|
if (!verbose) return;
|
||||||
|
va_start(args, format);
|
||||||
|
colprintf( "*", CYAN, format, &args);
|
||||||
|
va_end( args );
|
||||||
|
}
|
||||||
|
|
||||||
void err( char* format, ... ) {
|
void err( char* format, ... ) {
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
|
@ -107,19 +196,29 @@ int readresult(hid_device *dev, uint8_t *retbuf) {
|
||||||
int totbytes = 0;
|
int totbytes = 0;
|
||||||
int wantbytes = 8;
|
int wantbytes = 8;
|
||||||
time_t starttime;
|
time_t starttime;
|
||||||
int timeoutms = 2000;
|
double timeoutsecs = 0.5;
|
||||||
|
double timeoutms = timeoutsecs*1000;
|
||||||
starttime = time(NULL);
|
starttime = time(NULL);
|
||||||
|
|
||||||
while (totbytes != wantbytes) {
|
while (totbytes != wantbytes) {
|
||||||
int thisbytes = 0;
|
int thisbytes = 0;
|
||||||
thisbytes = hid_read_timeout(dev, &retbuf[totbytes], wantbytes + 1 - totbytes, timeoutms);
|
thisbytes = hid_read_timeout(dev, &retbuf[totbytes], wantbytes + 1 - totbytes, timeoutms);
|
||||||
if (thisbytes < 0) {
|
if (thisbytes < 0) {
|
||||||
err("Incomplete read from usb (got %d bytes, want %d bytes): %ls",totbytes,wantbytes, hid_error(dev));
|
warn("Incomplete read from usb (got %d bytes, want %d bytes): %ls",totbytes,wantbytes, hid_error(dev));
|
||||||
return E_BADREAD;
|
return E_BADREAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
totbytes += thisbytes;
|
totbytes += thisbytes;
|
||||||
if (time(NULL) - starttime > (timeoutms*1000)) {
|
if (thisbytes > 0) {
|
||||||
|
vinfo("Got %d/%d bytes",totbytes,wantbytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thisbytes > wantbytes) {
|
||||||
|
warn("Bad read from usb (got %d bytes, want %d bytes): %ls",totbytes,wantbytes, hid_error(dev));
|
||||||
|
return E_BADREAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (time(NULL) - starttime > timeoutsecs) {
|
||||||
warn("Timeout reading from usb");
|
warn("Timeout reading from usb");
|
||||||
return E_TIMEOUT;
|
return E_TIMEOUT;
|
||||||
}
|
}
|
||||||
|
@ -127,34 +226,53 @@ int readresult(hid_device *dev, uint8_t *retbuf) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void showlevel(uint8_t *buf) {
|
void dooutput(uint8_t *buf) {
|
||||||
uint16_t decibels;
|
double decibels;
|
||||||
uint8_t flags, rangeidx;
|
uint8_t flags, rangeidx;
|
||||||
time_t now;
|
time_t now;
|
||||||
char *longformat = "%12s: %ld\n";
|
char *longformat = "%12s: %ld\n";
|
||||||
char *strformat = "%12s: %s\n";
|
char *strformat = "%12s: %s\n";
|
||||||
char *dbformat = "%12s: %4.2f %s\n";
|
char *dbformat = "%12s: %4.2f %s\n";
|
||||||
decibels = buf[0] << 8 | buf[1];
|
char range[BUFLENSMALL];
|
||||||
|
char dbunits[BUFLENSMALL];
|
||||||
|
char checkmode[BUFLENSMALL];
|
||||||
|
decibels = getdecibels(buf);
|
||||||
flags = buf[2];
|
flags = buf[2];
|
||||||
rangeidx = buf[2] & 0xf;
|
rangeidx = buf[2] & 0xf;
|
||||||
now = time(NULL);
|
now = time(NULL);
|
||||||
|
strncpy(dbunits, flags & FLAG_DBCMODE ? "dBC" : "dBA", BUFLENSMALL);
|
||||||
|
strncpy(checkmode, flags & FLAG_FASTMODE ? "Fast" : "Slow", BUFLENSMALL);
|
||||||
|
strncpy(range, rangeidx > 0x4 ? "unknown" : rangestr[rangeidx], BUFLENSMALL);
|
||||||
|
|
||||||
|
if (outmode & OM_LONG) {
|
||||||
printf(longformat, "Time", now);
|
printf(longformat, "Time", now);
|
||||||
printf(dbformat, "Level", (double)decibels/10.0, flags & FLAG_DBCMODE ? "dBC" : "dBA");
|
printf(dbformat, "Level", decibels, dbunits);
|
||||||
printf(strformat, "Mode", flags & FLAG_FASTMODE ? "Fast" : "Slow");
|
printf(strformat, "Mode", checkmode);
|
||||||
printf(strformat, "Range", rangeidx > 0x4 ? "unknown" : rangestr[rangeidx]);
|
printf(strformat, "Range", range);
|
||||||
|
}
|
||||||
|
if (outmode & OM_SHORT) {
|
||||||
|
printf("time=[%ld] level=[%4.2f %s] mode=[%s] range=[%s]\n",
|
||||||
|
now, decibels, dbunits, checkmode, range);
|
||||||
|
}
|
||||||
|
if (outmode & OM_DB) {
|
||||||
|
influx_write_decibels(decibels);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int tcpconnect(char *hname, int port) {
|
int tcpconnect(char *hname, int port) {
|
||||||
int sockfd;
|
int sockfd;
|
||||||
struct sockaddr_in servaddr;
|
struct sockaddr_in servaddr;
|
||||||
struct hostent *hptr;
|
struct hostent *hptr;
|
||||||
|
fd_set fdset;
|
||||||
|
struct timeval tv;
|
||||||
|
int flags;
|
||||||
|
|
||||||
hptr = resolve(hname);
|
hptr = resolve(hname);
|
||||||
if (!hptr) return TRUE;
|
if (!hptr) return -1;
|
||||||
|
|
||||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
if (sockfd < 0) {
|
if (sockfd < 0) {
|
||||||
|
err("socket");
|
||||||
perror("socket()");
|
perror("socket()");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -162,11 +280,34 @@ int tcpconnect(char *hname, int port) {
|
||||||
servaddr.sin_port = htons(8086);
|
servaddr.sin_port = htons(8086);
|
||||||
memcpy(&servaddr.sin_addr, hptr->h_addr_list[0], hptr->h_length);
|
memcpy(&servaddr.sin_addr, hptr->h_addr_list[0], hptr->h_length);
|
||||||
bzero(servaddr.sin_zero, sizeof(servaddr.sin_zero));
|
bzero(servaddr.sin_zero, sizeof(servaddr.sin_zero));
|
||||||
|
|
||||||
|
fcntl(sockfd, F_SETFL, O_NONBLOCK);
|
||||||
|
|
||||||
if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) {
|
if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) {
|
||||||
printf("errno is %d\n", errno);
|
if (errno == EINPROGRESS) {
|
||||||
|
FD_ZERO(&fdset);
|
||||||
|
FD_SET(sockfd, &fdset);
|
||||||
|
tv.tv_sec = 2;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
if (select(sockfd+1, NULL, &fdset, NULL, &tv) == 1) {
|
||||||
|
int so_error;
|
||||||
|
socklen_t len = sizeof so_error;
|
||||||
|
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &so_error, &len);
|
||||||
|
if (so_error != 0) {
|
||||||
|
perror("getsockopt(d)");
|
||||||
|
close(sockfd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
perror("tcpconnect()");
|
perror("tcpconnect()");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flags = fcntl(sockfd, F_GETFL);
|
||||||
|
fcntl(sockfd, F_SETFL, flags & ~O_NONBLOCK);
|
||||||
|
|
||||||
return sockfd;
|
return sockfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +338,8 @@ int dohttp(char *hname, int port, char *header, char *body, char *retbuf) {
|
||||||
// get response
|
// get response
|
||||||
n = read(sockfd, response, sizeof(response));
|
n = read(sockfd, response, sizeof(response));
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
err("POST failed during response read");
|
err("POST failed during response read (n=%d)", n);
|
||||||
|
perror("read()");
|
||||||
close(sockfd);
|
close(sockfd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -244,11 +386,29 @@ struct hostent *resolve(char *hname) {
|
||||||
return hptr;
|
return hptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *append(char *orig, char *new, int maxlen) {
|
||||||
|
if (!orig) return NULL;
|
||||||
|
if (!new) return orig;
|
||||||
|
if (strlen(orig)) {
|
||||||
|
strncat(orig, ", ", maxlen);
|
||||||
|
strncat(orig, new, maxlen);
|
||||||
|
} else {
|
||||||
|
strncpy(orig, new, maxlen);
|
||||||
|
}
|
||||||
|
return orig;
|
||||||
|
}
|
||||||
|
|
||||||
int influx_init(char *hname, char *db, char *user, char *pass) {
|
int influx_init(char *hname, char *db, char *user, char *pass) {
|
||||||
strncpy(influxhost, hname, BUFLENSMALL);
|
char missing[BUFLEN];
|
||||||
strncpy(influxdb, db, BUFLENSMALL);
|
strncpy(missing, "", BUFLEN);
|
||||||
strncpy(influxuser, user, BUFLENSMALL);
|
if (!hname) append(missing, "hostname (-H)", BUFLEN);
|
||||||
strncpy(influxpass, pass, BUFLENSMALL);
|
if (!db) append(missing, "dbname (-d)", BUFLEN);
|
||||||
|
if (!user) append(missing, "username (-u)", BUFLEN);
|
||||||
|
if (!pass) append(missing, "password (-p)", BUFLEN);
|
||||||
|
if (strlen(missing)) {
|
||||||
|
err("missing influxdb details: %s", missing);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +418,7 @@ int influx_cmd(enum influxcmdtype cmdtype, char *cmd, char *retbuf) {
|
||||||
int rc = -1;
|
int rc = -1;
|
||||||
|
|
||||||
if (cmdtype == I_WRITE) {
|
if (cmdtype == I_WRITE) {
|
||||||
snprintf(header, BUFLEN, "POST /write?db=%s&u=%s&p=%s HTTP/1.1\r\nHost: influx:8086\r\nContent-Length: %ld\r\n\r\n", influxdb, influxuser, influxpass, strlen(cmd));
|
snprintf(header, BUFLEN, "POST /write?db=%s&u=%s&p=%s&precision=s HTTP/1.1\r\nHost: influx:8086\r\nContent-Length: %ld\r\n\r\n", influxdb, influxuser, influxpass, strlen(cmd));
|
||||||
snprintf(newcmd,BUFLEN,"%s",cmd);
|
snprintf(newcmd,BUFLEN,"%s",cmd);
|
||||||
} else if (cmdtype == I_READ) {
|
} else if (cmdtype == I_READ) {
|
||||||
snprintf(header, BUFLEN, "GET /query?db=%s&u=%s&p=%s HTTP/1.1\r\nHost: influx:8086\r\nContent-Length: %ld\r\n\r\n", influxdb, influxuser, influxpass, strlen(cmd)+2);
|
snprintf(header, BUFLEN, "GET /query?db=%s&u=%s&p=%s HTTP/1.1\r\nHost: influx:8086\r\nContent-Length: %ld\r\n\r\n", influxdb, influxuser, influxpass, strlen(cmd)+2);
|
||||||
|
@ -268,12 +428,12 @@ int influx_cmd(enum influxcmdtype cmdtype, char *cmd, char *retbuf) {
|
||||||
strncpy(newcmd,"",BUFLEN);
|
strncpy(newcmd,"",BUFLEN);
|
||||||
} else {
|
} else {
|
||||||
err("Invalid influx command type (%d)", cmdtype);
|
err("Invalid influx command type (%d)", cmdtype);
|
||||||
return TRUE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = dohttp(influxhost, 8086, header, newcmd, retbuf);
|
rc = dohttp(influxhost, 8086, header, newcmd, retbuf);
|
||||||
if (rc != 204) {
|
if (rc != 204) {
|
||||||
err("Influx write to %s/%s failed (HTTP %d)",influxhost,influxdb,rc);
|
err("Influx write to %s:8086 db %s failed (HTTP %d)",influxhost,influxdb,rc);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
@ -291,16 +451,42 @@ int influx_ping(char *retbuf) {
|
||||||
int rv;
|
int rv;
|
||||||
rv = influx_cmd(I_PING, "", retbuf);
|
rv = influx_cmd(I_PING, "", retbuf);
|
||||||
if (!rv) {
|
if (!rv) {
|
||||||
info("Ping success:\n%s\n", retbuf);
|
vinfo("Ping success");
|
||||||
}
|
}
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
int influx_write_decibels(double decibels) {
|
int influx_write_decibels(double decibels) {
|
||||||
char cmd[BUFLEN];
|
char cmd[BUFLEN];
|
||||||
snprintf(cmd, BUFLEN, "volume decibels=%0.1f\n", decibels);
|
time_t now = time(NULL);
|
||||||
|
snprintf(cmd, BUFLEN, "volume decibels=%0.1f %ld\n", decibels, now);
|
||||||
|
if (doit) {
|
||||||
influx_insert(cmd, NULL);
|
influx_insert(cmd, NULL);
|
||||||
return 0;
|
} else {
|
||||||
|
info("Would have done: %s", cmd);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getdecibels(uint8_t *buf) {
|
||||||
|
if (!buf) return -1;
|
||||||
|
if (strlen((char *)buf) < 2) return -1;
|
||||||
|
return (buf[0] << 8 | buf[1])/10;
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_signal(int signum) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanup(void) {
|
||||||
|
info("Cleaning up...\n");
|
||||||
|
if (dev) hid_close(dev);
|
||||||
|
if (influxhost) free(influxhost);
|
||||||
|
if (influxdb) free(influxdb);
|
||||||
|
if (influxuser) free(influxuser);
|
||||||
|
if (influxpass) free(influxpass);
|
||||||
|
|
||||||
|
hid_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
@ -308,11 +494,28 @@ int main(int argc, char *argv[]) {
|
||||||
//hid_device *handle;
|
//hid_device *handle;
|
||||||
uint8_t capture_cmd[8] = { CMD_CAPTURE };
|
uint8_t capture_cmd[8] = { CMD_CAPTURE };
|
||||||
uint8_t buf[8];
|
uint8_t buf[8];
|
||||||
char retbuf[BUFLEN];
|
char retbuf[BUFLEN],path[BUFLEN];
|
||||||
int res;
|
int i, finished = 0;
|
||||||
int i;
|
int interval = 0;
|
||||||
|
int useinflux = FALSE;
|
||||||
enum mode_enum { M_PROBE, M_TEST, M_TESTDB } mode = M_PROBE;
|
enum mode_enum { M_PROBE, M_TEST, M_TESTDB } mode = M_PROBE;
|
||||||
hid_device *dev = NULL;
|
struct timeval seed;
|
||||||
|
|
||||||
|
gettimeofday(&seed, NULL);
|
||||||
|
|
||||||
|
// generate magic id in bytes 2-4
|
||||||
|
srand(seed.tv_usec);
|
||||||
|
for (i = 1; i <= 3; i++) {
|
||||||
|
capture_cmd[i] = rand() % 256;
|
||||||
|
}
|
||||||
|
for (i = 4; i <= 7; i++) {
|
||||||
|
capture_cmd[i] = 0;
|
||||||
|
}
|
||||||
|
printf("cap cmd is:\n");
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
printf(" 0x%01x\n",capture_cmd[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
// handle args
|
// handle args
|
||||||
for (i=1; i<argc;i++) {
|
for (i=1; i<argc;i++) {
|
||||||
|
@ -320,46 +523,175 @@ int main(int argc, char *argv[]) {
|
||||||
mode = M_TESTDB;
|
mode = M_TESTDB;
|
||||||
} else if (!strcmp(argv[i], "-t")) {
|
} else if (!strcmp(argv[i], "-t")) {
|
||||||
mode = M_TEST;
|
mode = M_TEST;
|
||||||
|
} else if (!strcmp(argv[i], "-v")) {
|
||||||
|
verbose = TRUE;
|
||||||
|
} else if (!strcmp(argv[i], "-o")) {
|
||||||
|
i++;
|
||||||
|
if (i < argc && argv[i]) {
|
||||||
|
if (!strcmp(argv[i], "db")) {
|
||||||
|
outmode |= OM_DB;
|
||||||
|
} else if (!strcmp(argv[i], "short")) {
|
||||||
|
outmode |= OM_SHORT;
|
||||||
|
} else if (!strcmp(argv[i], "long")) {
|
||||||
|
outmode |= OM_LONG;
|
||||||
|
} else {
|
||||||
|
err("Invalid output mode '%s'", argv[i]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err("outputmode not provided\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else if (!strcmp(argv[i], "-i")) {
|
||||||
|
i++;
|
||||||
|
if (i < argc && argv[i]) {
|
||||||
|
interval = atoi(argv[i]);
|
||||||
|
if (interval < 2) {
|
||||||
|
err("Wait interval must be >= 2\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err("Wait interval not provided\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else if (!strcmp(argv[i], "-y")) {
|
||||||
|
doit = TRUE;
|
||||||
|
} else if (!strcmp(argv[i], "-H")) {
|
||||||
|
i++;
|
||||||
|
if (i < argc && argv[i]) {
|
||||||
|
influxhost = strdup(argv[i]);
|
||||||
|
} else {
|
||||||
|
err("influxdb host not provided\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else if (!strcmp(argv[i], "-d")) {
|
||||||
|
i++;
|
||||||
|
if (i < argc && argv[i]) {
|
||||||
|
influxdb = strdup(argv[i]);
|
||||||
|
} else {
|
||||||
|
err("influxdb dbname not provided\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else if (!strcmp(argv[i], "-u")) {
|
||||||
|
i++;
|
||||||
|
if (i < argc && argv[i]) {
|
||||||
|
influxuser = strdup(argv[i]);
|
||||||
|
} else {
|
||||||
|
err("influxdb username not provided\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else if (!strcmp(argv[i], "-p")) {
|
||||||
|
i++;
|
||||||
|
if (i < argc && argv[i]) {
|
||||||
|
influxpass = strdup(argv[i]);
|
||||||
|
} else {
|
||||||
|
err("influxdb password not provided\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err("Invalid argument '%s'\n", argv[i]);
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
influx_init("gridbug.nethack.net", "haven", "", "");
|
if (atexit(cleanup)) {
|
||||||
|
perror("atexit()");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
signal(SIGINT, handle_signal);
|
||||||
|
|
||||||
|
|
||||||
|
if (!outmode) {
|
||||||
|
outmode = OM_SHORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outmode & OM_DB) {
|
||||||
|
useinflux = TRUE;
|
||||||
|
} else if (mode == M_TESTDB) {
|
||||||
|
useinflux = TRUE;
|
||||||
|
} else {
|
||||||
|
useinflux = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useinflux) {
|
||||||
|
if (influx_init(influxhost, influxdb, influxuser, influxpass)) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
if (mode == M_TESTDB) {
|
if (mode == M_TESTDB) {
|
||||||
printf("running ping...\n");
|
verbose = TRUE;
|
||||||
influx_ping(retbuf);
|
}
|
||||||
|
vinfo("Running influxdb http PING...");
|
||||||
|
if (influx_ping(retbuf)) {
|
||||||
|
err("Couldn't acess influxdb at host=%s:8086 db=%s",influxhost,influxdb);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
if (mode == M_TESTDB) {
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (hid_init()) {
|
if (hid_init()) {
|
||||||
perror("hid_init()");
|
perror("hid_init()");
|
||||||
err("Failed to initialise hidapi.");
|
err("Failed to initialise hidapi.");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
dev = hid_open(MIC_VID, MIC_PID, NULL);
|
dev = hid_open(hextoint(MIC_VID), hextoint(MIC_PID), NULL);
|
||||||
|
|
||||||
if (mode == M_TEST) {
|
if (mode == M_TEST) {
|
||||||
if (dev) {
|
verbose = TRUE;
|
||||||
info("Found Benestar USB sound meter (vendor 0x%04hx, product 0x%04hx).", MIC_VID, MIC_PID);
|
|
||||||
exit(0);
|
|
||||||
}
|
}
|
||||||
|
if (dev) {
|
||||||
|
vinfo("Found Benestar USB sound meter (vendor 0x%04hx, product 0x%04hx).", MIC_VID, MIC_PID);
|
||||||
|
getdevpath(hextoint(MIC_VID), hextoint(MIC_PID), path);
|
||||||
|
vinfo("Full device path is: %s", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
err("Benestar USB sound meter not found (vendor 0x%04hx, product 0x%04hx).", MIC_VID, MIC_PID);
|
err("Benestar USB sound meter not found (vendor 0x%04hx, product 0x%04hx).", MIC_VID, MIC_PID);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
if (mode == M_TEST) {
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!finished) {
|
||||||
|
int rv = 0,donereading = 0,ntimeouts = 0;;
|
||||||
|
vinfo("Asking for status");
|
||||||
// get current reading
|
// get current reading
|
||||||
res = hid_write(dev, capture_cmd, 9);
|
hid_write(dev, capture_cmd, 9);
|
||||||
|
|
||||||
// wait for response
|
// wait for response
|
||||||
|
vinfo("Waiting for response");
|
||||||
|
while (!donereading) {
|
||||||
// see: https://github.com/pvachon/gm1356/blob/master/splread.c
|
// see: https://github.com/pvachon/gm1356/blob/master/splread.c
|
||||||
res = E_TIMEOUT;
|
rv = readresult(dev, buf);
|
||||||
while (res == E_TIMEOUT) {
|
if (rv) {
|
||||||
res = readresult(dev, buf);
|
// failure
|
||||||
|
ntimeouts++;
|
||||||
|
|
||||||
|
if (ntimeouts >= 3) {
|
||||||
|
// give up for this time
|
||||||
|
donereading = 1;
|
||||||
|
} else {
|
||||||
|
if (ntimeouts == 2) {
|
||||||
|
// after first retry, try resetting the usb device
|
||||||
|
resetusb(path);
|
||||||
|
}
|
||||||
|
// try again
|
||||||
|
hid_write(dev, capture_cmd, 9);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// success
|
||||||
|
dooutput(buf);
|
||||||
|
donereading = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interval) {
|
||||||
|
sleep(interval);
|
||||||
|
} else {
|
||||||
|
finished = 1;
|
||||||
}
|
}
|
||||||
if (res == 0) {
|
|
||||||
showlevel(buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -372,9 +704,6 @@ int main(int argc, char *argv[]) {
|
||||||
except: None
|
except: None
|
||||||
*/
|
*/
|
||||||
|
|
||||||
hid_close(dev);
|
|
||||||
|
|
||||||
hid_exit();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
9
howard.h
9
howard.h
|
@ -1,12 +1,19 @@
|
||||||
enum influxcmdtype { I_READ, I_WRITE, I_PING };
|
enum influxcmdtype { I_READ, I_WRITE, I_PING };
|
||||||
|
|
||||||
|
char *getdevpath(int vid, int pid, char *retvar);
|
||||||
|
int resetusb(char *path);
|
||||||
|
char *append(char *orig, char *new, int maxlen);
|
||||||
void colprintf( char *prefix, const char *col, char* format, va_list *args );
|
void colprintf( char *prefix, const char *col, char* format, va_list *args );
|
||||||
|
double getdecibels(uint8_t *buf);
|
||||||
|
void cleanup(void);
|
||||||
|
void handle_signal(int signum);
|
||||||
void info( char* format, ... );
|
void info( char* format, ... );
|
||||||
|
void vinfo( char* format, ... );
|
||||||
void err( char* format, ... );
|
void err( char* format, ... );
|
||||||
void warn( char* format, ... );
|
void warn( char* format, ... );
|
||||||
int hextoint(char *hex);
|
int hextoint(char *hex);
|
||||||
int readresult(hid_device *dev, uint8_t *retbuf);
|
int readresult(hid_device *dev, uint8_t *retbuf);
|
||||||
void showlevel(uint8_t *buf);
|
void dooutput(uint8_t *buf);
|
||||||
int tcpconnect(char *hname, int port);
|
int tcpconnect(char *hname, int port);
|
||||||
int dohttp(char *hname, int port, char *header, char *body, char *retbuf);
|
int dohttp(char *hname, int port, char *header, char *body, char *retbuf);
|
||||||
struct hostent *resolve(char *hname);
|
struct hostent *resolve(char *hname);
|
||||||
|
|
Loading…
Reference in New Issue