Skip to content

Instantly share code, notes, and snippets.

@classilla
Created December 20, 2021 05:49
Show Gist options
  • Save classilla/4b8780e853e4d93eba8fa5ef789d9c47 to your computer and use it in GitHub Desktop.
Save classilla/4b8780e853e4d93eba8fa5ef789d9c47 to your computer and use it in GitHub Desktop.
Linux code for reading a GM1356 USB-based decibel sound monitor. See https://oldvcr.blogspot.com/2021/12/monitoring-vintage-server-room-and.html
/*
(C)2020-1 Cameron Kaiser, ckaiser@floodgap.com
All rights reserved.
Distributed under the Floodgap Free Software License.
Credit to https://github.com/dobra-noc/gm1356/blob/master/PROTOCOL.md for the
original protocol description.
Linux:
gcc -o gm1356 gm1356.c -lusb
This device doesn't register as a HID on Mac OS X (polling time of 0?!).
This device freaks out NetBSD and messes with the console.
Even Linux's libhid doesn't like it, so we have to talk to it the hard way.
*/
#define DEBUG 0
#define DSLM_VENDID 0x64bd
#define DSLM_DEVID 0x74e3
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#define SEND_PACKET_LENGTH 8
unsigned char PACKET[SEND_PACKET_LENGTH] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
int ret;
/* #if defined(__linux__) */
#include <usb.h>
/* this is written like this in the hopes that there will be ports to
other operating systems, but this device is a piece of crap and has
stymied all of my current attempts. */
struct usb_bus *busses;
struct usb_bus *bus;
struct usb_device *dev, *dslm = NULL;
struct usb_dev_handle *hdslm;
void release_dslm_handle() {
(void)usb_release_interface(hdslm, 1);
(void)usb_close(hdslm);
}
int claim_dslm_handle() {
if (DEBUG)
usb_set_debug(255);
usb_init();
usb_find_busses();
ret = usb_find_devices();
if (ret < 0) {
perror("usb_find_devices");
return 1;
}
busses = usb_get_busses();
for(bus = busses; bus; bus = bus->next) {
for(dev = bus->devices; dev; dev = dev->next) {
if ((dev->descriptor.idVendor == DSLM_VENDID) &&
(dev->descriptor.idProduct == DSLM_DEVID)) {
dslm = dev;
}
}
}
if (!dslm) {
fprintf(stderr, "unable to find DSLM\n");
return 1;
}
hdslm = usb_open(dslm);
usb_detach_kernel_driver_np(hdslm, 0);
ret = usb_claim_interface(hdslm, 0);
if (ret < 0) {
release_dslm_handle();
perror("usb_claim_interface");
return 1;
}
return 0;
}
/*
int usb_interrupt_write(usb_dev_handle *dev, int ep, const char *bytes,
int size, int timeout);
*/
int send_dslm_packet() {
ret = usb_interrupt_write(
hdslm,
0x02, /* endpoint 2 OUT */
(const char *)PACKET,
SEND_PACKET_LENGTH,
10000);
if (DEBUG) {
fprintf(stderr, "usb_interrupt_write: %d\n", ret);
if (ret != SEND_PACKET_LENGTH)
return 1;
} else if (ret != SEND_PACKET_LENGTH) {
fprintf(stderr, "usb_interrupt_write: %d\n", ret);
return 1;
}
return 0;
}
/*
int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size,
int timeout);
*/
int get_dslm_report() {
ret = usb_interrupt_read(
hdslm,
0x81, /* endpoint 1 IN */
(char *)PACKET,
SEND_PACKET_LENGTH,
10000);
if (DEBUG) {
fprintf(stderr, "usb_interrupt_read: %d\n", ret);
}
return ret;
}
/* #endif */
void usage(char **argv) {
fprintf(stderr, "usage: %s [-s] [-c] [-m] [-r 0-4]\n", argv[0]);
}
int main(int argc, char** argv) {
int ch;
size_t tries = 0;
float db, maxdb = 130.0f;
long r;
/* send settings */
PACKET[0] = 0x56;
PACKET[1] = 0x40; /* filter A, not max, fast, full 30-130dB range */
while ((ch = getopt(argc, argv, "?hr:csm")) != -1) {
switch(ch) {
case 's': {
/* use 1 second averaged sample, not instant */
PACKET[1] &= 0xbf; /* turn off fast bit */
break;
}
case 'c': {
/* use low-pass filter "filter C" */
PACKET[1] |= 0x10; /* turn on C bit */
break;
}
case 'm': {
/* return max reading rather than current */
PACKET[1] |= 0x20; /* turn on max bit */
break;
}
case 'r': {
/* set decibel range for greater precision */
/*
0: 30-130dB range
1: 30-60dB
2: 50-100dB
3: 60-110dB
4: 80-130dB
*/
ret = atoi(optarg);
if (ret < 0 || ret > 4) {
fprintf(stderr, "%s: -r 0-4 only\n",
argv[0]);
return 1;
}
maxdb =
(maxdb == 1) ? 60 :
(maxdb == 2) ? 100 :
(maxdb == 3) ? 110 :
130;
PACKET[1] |= ret;
break;
}
case '?' :
case 'h' :
default: {
usage(argv);
return 0;
}
}
}
if (claim_dslm_handle())
return 1;
if (send_dslm_packet())
return 1;
/* request dB */
PACKET[0] = 0xb3;
/* needs a random ID number */
r = random();
PACKET[1] = r & 0xff;
PACKET[2] = ( r >> 8 ) & 0xff;
PACKET[3] = ( r >>16 ) & 0xff;
if (DEBUG) {
for(ret=0;ret<8;ret++) {
fprintf(stderr, "%02x ", PACKET[ret]);
}
}
for(;;) {
/* sometimes have to try this a few times */
if (send_dslm_packet())
return 1;
if (get_dslm_report() == SEND_PACKET_LENGTH) {
if (DEBUG) {
for(ret=0;ret<8;ret++) {
fprintf(stderr, "%02x ", PACKET[ret]);
}
}
/* check for preposterous values: cannot be > range */
db = ((256*PACKET[0]) + PACKET[1]) / 10.0f;
if (db <= maxdb)
break;
if (DEBUG)
fprintf(stderr, "preposterous value %2.1f\n",
db);
}
if (++tries > 8) {
fprintf(stderr, "read failed\n");
release_dslm_handle();
return 1;
}
}
fprintf(stdout, "%2.1f\n", db);
release_dslm_handle();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment