#include #include #ifdef _LINUX #include #include #endif #ifdef _TERMUX #include #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "howard.h" #define TRUE (-1) #define FALSE (0) #define BUFLEN 8196 #define BUFLENSMALL 255 // Benestar sound meter //#define MIC_VID (0x1630) //#define MIC_PID (0x05dc) #define MIC_VID "64bd" #define MIC_PID "74e3" #define CMD_CAPTURE (0xb3) hid_device *dev = NULL; const char *rangestr[] = { "30-130", "30-80", "50-100", "60-110", "80-130", }; char *influxhost = NULL; char *influxdb = NULL; char *influxuser = NULL; 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_FASTMODE (0x40) #define E_TIMEOUT -1 #define E_BADREAD -2 // ANSI stuff #define BOLD "\x1b[1m" #define ITALIC "\x1b[3m" #define STRIKE "\x1b[9m" #define PLAIN "\x1b[0m" #define UNDERLINE "\x1b[4m" #define RED "\x1b[31m" #define MAGENTA "\x1b[35m" #define GREEN "\x1b[32m" #define YELLOW "\x1b[33m" #define BLUE "\x1b[34m" #define CYAN "\x1b[36m" #define GREY "\x1b[2;37m" void colprintf( char *prefix, const char *col, char *format, va_list *args ) { printf("%s%s%s %s%s",col,BOLD,prefix,PLAIN,col); vprintf(format, *args); 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, ... ) { va_list args; va_start(args, format); colprintf( "*", CYAN, format, &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, ... ) { va_list args; va_start(args, format); colprintf( "ERROR:", RED, format, &args); va_end( args ); } void warn( char* format, ... ) { va_list args; va_start(args, format); colprintf( "WARNING:", YELLOW, format, &args); va_end( args ); } int hextoint(char *hex) { int base=16; if (strstr(hex, "0x") == hex) { base=0; } return strtol(hex, NULL, base); } int readresult(hid_device *dev, uint8_t *retbuf) { int totbytes = 0; int wantbytes = 8; time_t starttime; double timeoutsecs = 0.5; double timeoutms = timeoutsecs*1000; starttime = time(NULL); while (totbytes != wantbytes) { int thisbytes = 0; thisbytes = hid_read_timeout(dev, &retbuf[totbytes], wantbytes + 1 - totbytes, timeoutms); if (thisbytes < 0) { warn("Incomplete read from usb (got %d bytes, want %d bytes): %ls",totbytes,wantbytes, hid_error(dev)); return E_BADREAD; } totbytes += thisbytes; 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"); return E_TIMEOUT; } } return 0; } void dooutput(uint8_t *buf) { double decibels; uint8_t flags, rangeidx; time_t now; char *longformat = "%12s: %ld\n"; char *strformat = "%12s: %s\n"; char *dbformat = "%12s: %4.2f %s\n"; char range[BUFLENSMALL]; char dbunits[BUFLENSMALL]; char checkmode[BUFLENSMALL]; decibels = getdecibels(buf); flags = buf[2]; rangeidx = buf[2] & 0xf; 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(dbformat, "Level", decibels, dbunits); printf(strformat, "Mode", checkmode); 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 sockfd; struct sockaddr_in servaddr; struct hostent *hptr; fd_set fdset; struct timeval tv; int flags; hptr = resolve(hname); if (!hptr) return -1; sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { err("socket"); perror("socket()"); return -1; } servaddr.sin_family = hptr->h_addrtype; servaddr.sin_port = htons(8086); memcpy(&servaddr.sin_addr, hptr->h_addr_list[0], hptr->h_length); bzero(servaddr.sin_zero, sizeof(servaddr.sin_zero)); fcntl(sockfd, F_SETFL, O_NONBLOCK); if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) { 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()"); return -1; } } flags = fcntl(sockfd, F_GETFL); fcntl(sockfd, F_SETFL, flags & ~O_NONBLOCK); return sockfd; } int dohttp(char *hname, int port, char *header, char *body, char *retbuf) { char response[BUFLEN]; char *p,*dp,retcode_str[BUFLENSMALL]; ssize_t n; int sockfd,retcode_int = -1; sockfd = tcpconnect(hname,port); if (sockfd < 0) { err("TCP connect to %s:%d failed", hname, port); return TRUE; } if (write(sockfd, header, strlen(header)) < 0) { err("POST failed during header send"); close(sockfd); return -1; } if (write(sockfd, body, strlen(body)) < 0) { err("POST failed during body send"); close(sockfd); return -1; } // get response n = read(sockfd, response, sizeof(response)); if (n < 0) { err("POST failed during response read (n=%d)", n); perror("read()"); close(sockfd); return -1; } response[n] = 0; // NUL-terminate close(sockfd); if (retbuf) strncpy(retbuf, response, BUFLEN); // HTTP/1.x 204 retcode_int = -1; p = strstr(response, "HTTP/1"); if (p) { p += 9; // should now be at start of return code dp = retcode_str; for ( ; (p + 3 < (response + strlen(response))) && *p && isdigit(*p); p++,dp++) { *dp = *p; } *dp = 0; retcode_int = atoi(retcode_str); } else { retcode_int = -1; } return retcode_int; } struct hostent *resolve(char *hname) { struct hostent *hptr; hptr = gethostbyname(hname); if (hptr == NULL) { err("Failed to resolve %s (%s)",hname,hstrerror(h_errno)); return NULL; } if (hptr->h_addrtype != AF_INET) { err("No IN records for %s.",hname); return NULL; } if (hptr->h_addr_list == NULL) { err("No A records for %s.",hname); return NULL; } 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) { char missing[BUFLEN]; strncpy(missing, "", BUFLEN); if (!hname) append(missing, "hostname (-H)", BUFLEN); 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; } int influx_cmd(enum influxcmdtype cmdtype, char *cmd, char *retbuf) { char header[BUFLEN]; char newcmd[BUFLEN+2]; int rc = -1; if (cmdtype == I_WRITE) { 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); } 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(newcmd,BUFLEN,"q=%s",cmd); } else if (cmdtype == I_PING) { snprintf(header, BUFLEN, "GET /ping HTTP/1.1\r\nHost: influx:8086\r\n\r\n"); strncpy(newcmd,"",BUFLEN); } else { err("Invalid influx command type (%d)", cmdtype); return FALSE; } rc = dohttp(influxhost, 8086, header, newcmd, retbuf); if (rc != 204) { err("Influx write to %s:8086 db %s failed (HTTP %d)",influxhost,influxdb,rc); return TRUE; } return FALSE; } int influx_insert(char *cmd, char *retbuf) { return influx_cmd(I_WRITE, cmd, retbuf); } int influx_query(char *cmd, char *retbuf ) { return influx_cmd(I_READ, cmd, retbuf); } int influx_ping(char *retbuf) { int rv; rv = influx_cmd(I_PING, "", retbuf); if (!rv) { vinfo("Ping success"); } return rv; } int influx_write_decibels(double decibels) { char cmd[BUFLEN]; time_t now = time(NULL); snprintf(cmd, BUFLEN, "volume decibels=%0.1f %ld\n", decibels, now); if (doit) { influx_insert(cmd, NULL); } 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[]) { //unsigned char buf[65]; //hid_device *handle; uint8_t capture_cmd[8] = { CMD_CAPTURE }; uint8_t buf[8]; char retbuf[BUFLEN],path[BUFLEN]; int i, finished = 0; int interval = 0; int useinflux = FALSE; enum mode_enum { M_PROBE, M_TEST, M_TESTDB } mode = M_PROBE; 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 for (i=1; i= 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); } } 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) { verbose = TRUE; } vinfo("Running influxdb http PING..."); if (influx_ping(retbuf)) { err("Couldn't acess influxdb at host=%s:8086 db=%s",influxhost,influxdb); exit(1); } if (mode == M_TESTDB) { exit(0); } } if (hid_init()) { perror("hid_init()"); err("Failed to initialise hidapi."); exit(1); } dev = hid_open(hextoint(MIC_VID), hextoint(MIC_PID), NULL); if (mode == M_TEST) { verbose = TRUE; } 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) { err("Benestar USB sound meter not found (vendor 0x%04hx, product 0x%04hx).", MIC_VID, MIC_PID); exit(1); } if (mode == M_TEST) { exit(0); } while (!finished) { int rv = 0,donereading = 0,ntimeouts = 0;; vinfo("Asking for status"); // get current reading hid_write(dev, capture_cmd, 9); // wait for response vinfo("Waiting for response"); while (!donereading) { // see: https://github.com/pvachon/gm1356/blob/master/splread.c rv = readresult(dev, buf); if (rv) { // 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; } } /* ret = dev.ctrl_transfer(0xC0,4,0,0,200) dB = (ret[0]+((ret[1]&3)*256))*0.1+30 print dB msg="{'dB':'"+str(dB)+"'}" try: requests.post('https://temporacloud.com/connection/clientSend', data={'streams':streams,'tokens':tokens,'message':msg},verify=False) except: None */ return 0; } /* // Initialize the hidapi library res = hid_init(); // Open the device using the VID, PID, // and optionally the Serial number. handle = hid_open(0x4d8, 0x3f, NULL); // Read the Manufacturer String res = hid_get_manufacturer_string(handle, wstr, MAX_STR); wprintf(L"Manufacturer String: %s\n", wstr); // Read the Product String res = hid_get_product_string(handle, wstr, MAX_STR); wprintf(L"Product String: %s\n", wstr); // Read the Serial Number String res = hid_get_serial_number_string(handle, wstr, MAX_STR); wprintf(L"Serial Number String: (%d) %s\n", wstr[0], wstr); // Read Indexed String 1 res = hid_get_indexed_string(handle, 1, wstr, MAX_STR); wprintf(L"Indexed String 1: %s\n", wstr); // Toggle LED (cmd 0x80). The first byte is the report number (0x0). buf[0] = 0x0; buf[1] = 0x80; res = hid_write(handle, buf, 65); // Request state (cmd 0x81). The first byte is the report number (0x0). buf[0] = 0x0; buf[1] = 0x81; res = hid_write(handle, buf, 65); // Read requested state res = hid_read(handle, buf, 65); // Print out the returned buffer. for (i = 0; i < 4; i++) printf("buf[%d]: %d\n", i, buf[i]); // Close the device hid_close(handle); // Finalize the hidapi library res = hid_exit(); */