Click here to Skip to main content
15,885,278 members
Articles / Programming Languages / C

USB Digital Multimeter Driver Using HIDAPI

Rate me:
Please Sign up or sign in to vote.
4.84/5 (20 votes)
3 Dec 2013CPOL2 min read 61.2K   4K   39  
This driver program decodes and displays LCD output from a Victor 86B USB DMM data packet that has been accessed using HIDAPI functions.
//
// name    Victor86B_usbDMM.cpp
// author  david moloney, movidius ltd.
// purpose decodes and displays output from Victor 86B USB DMM
// date    03 Jan 2012
// note    based on HIDAPI http://www.signal11.us/oss/hidapi/
//

#pragma warning(disable : 4996) // disable MSVC++ printf warning

#include <stdio.h>
#include <wchar.h>
#include <string.h>
#include <stdlib.h>
#include "hidapi.h"
#include "time.h"


/* Victor 86B DMM on my machine returns following descriptor

  Device Found
  type: 1244 d237
  path: \\?\hid#vid_1244&pid_d237#9&29fd82b3&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
  serial_number:
  Manufacturer:
  Product:
  Release:      100
  Interface:    -1
*/

void report_HIDs() {

	struct hid_device_info *devs, *cur_dev;
	
	devs = hid_enumerate(0x0, 0x0);
	cur_dev = devs;	
	while (cur_dev) {
		printf("Device Found\n  type: %04hx %04hx\n  path: %s\n  serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number);
		printf("\n");

		if (cur_dev->vendor_id==0x1244) printf("  Manufacturer: Victor\n");
		else                            printf("  Manufacturer: %ls\n", cur_dev->manufacturer_string);
		
		if (cur_dev->product_id==0xd237) printf("  Product:      86B DMM\n");
		else                             printf("  Product:      %ls\n", cur_dev->product_string);
		
		printf("  Release:      %hx\n", cur_dev->release_number);
		printf("  Interface:    %d\n",  cur_dev->interface_number);
		printf("\n");
		cur_dev = cur_dev->next;
	}
	hid_free_enumeration(devs);

} // report_HIDs()


struct Victor86B_display { // Victor 86B LCD Display descriptor
  /*

  AUTO RS232 REL HOLD BATT

  DC  -   -   -   -
  AC | | | | | | | |  % C
   -  -   -   -   -   < >
     | | | | | | | |  n m F V A  
	  - . - . - . -     μ
	                  M k Ω Hz        
  */

  int digit[4], decimal[4], minus;
  int AUTO, RS232, REL, HOLD, BATT;
  int AC, DC;
  int Percent, degC;
  int Spkr, Diode;
  int nano, milli, Farads, V, A;
  int       micro;
  int Mega, Kilo, Ohms, Hertz;
  int Overload;

}; // struct Victor86B_display


struct DMM_reading { // DMM_reading
  double value; // value of reading
  int    units; // Volts, Amps, oC etc.
  int    info;  // Overload, REL, HOLD etc.
}; // struct DMM_reading


struct DMM_reading decode_Victor86B(unsigned char* buf) {

  // Victor86B multimeter LCD control bits passed back as a string of 14 hex characters
  // details derived experimentally using the Victor86B DMM and a Bench PSU

  struct DMM_reading DMM;

  int LCD_minus=0;
  int LCD_digit[4];
  int LCD_decimal[4];
  int LCD_DC   = 0; // DC
  int LCD_AC   = 0; // AC
  int LCD_V    = 0; // Volts
  int LCD_A    = 0; // Amps
  int LCD_m    = 0; // m - milliamps
  int LCD_u    = 0; // u - microamps
  int LCD_REL  = 0; // REL
  int LCD_HOLD = 0; // HOLD
  int LCD_Hz   = 0; // Hz
  int LCD_oC   = 0; // degrees C
  int LCD_M    = 0; // Mega - ohms  
  int LCD_OL   = 0; // Overload

  if (strlen((char*)buf)!=23) { // display error message if USB HID packet (buf) incomplete!
	//
	printf("no string read from DMM ... %d\n",strlen((char*)buf));
	printf("  1. make sure DMM is switched on\n");
	printf("  2. try plugging out USB and replacing\n");
	DMM.info = -1;
	return DMM;
  }
  else { // decode and display digits from USB HID packet (buf)
    
	// LCD       digits           annunciators
	//        -   -   -   - 
	//       | | | | | | | | 
	//     -  -   -   -   -       M            Hz     
	//       | | | | | | | |   DC REL          V
	//        -   -   -   -    AC HOLD m oC u  A
	//
	// buf  3,10 6,9 5,7 0,2   1  4    8 11 12 13

    // reset display variables
    LCD_minus=0;
    LCD_digit[0]=LCD_digit[1]=LCD_digit[2]=LCD_digit[3]=0;
    LCD_decimal[0]=LCD_decimal[1]=LCD_decimal[2]=LCD_decimal[3]=0;

    // LCD_digit[3] - most significant digit
  
    /* 
      digit 4 of Victor86B LCD display ... values are decimal!
	                    .
      LCD b3	b10		b10
	  0   33    79      95   
      1   17	111     127
      2   65	15      31
      3   97	239     255
      4   81	175     191
    */

    // +X
    if ((unsigned)buf[3]== 33 && (unsigned)buf[10]== 79) LCD_digit[3]=0;
    if ((unsigned)buf[3]== 17 && (unsigned)buf[10]==111) LCD_digit[3]=1;
    if ((unsigned)buf[3]== 65 && (unsigned)buf[10]== 15) LCD_digit[3]=2;
    if ((unsigned)buf[3]== 97 && (unsigned)buf[10]==239) LCD_digit[3]=3;
    if ((unsigned)buf[3]== 81 && (unsigned)buf[10]==175) LCD_digit[3]=4;
	// -X
	if ((unsigned)buf[3]== 33 && (unsigned)buf[10]== 95) { LCD_digit[3]=0; LCD_minus=1; }
    if ((unsigned)buf[3]== 17 && (unsigned)buf[10]==127) { LCD_digit[3]=1; LCD_minus=1; }
    if ((unsigned)buf[3]== 65 && (unsigned)buf[10]== 31) { LCD_digit[3]=2; LCD_minus=1; }
    if ((unsigned)buf[3]== 97 && (unsigned)buf[10]==255) { LCD_digit[3]=3; LCD_minus=1; }
    if ((unsigned)buf[3]== 81 && (unsigned)buf[10]==191) { LCD_digit[3]=4; LCD_minus=1; }

    // LCD_digit[2]

    /* 
    digit 3 of Victor86B LCD display
                .   
    LCD b6	b9  b9
	0   47  69  85  
    1   31	101 117 
    2   79	5   21  
    3   111	229 245 
    4   95	165 181 
    5   239	37  53  
    6   239	69  85  
    7   31	229 245 
    8   111	69  85  
    9   111	37  53  

	[.] bit 5 of buf[9] controls the decimal-point which preceeds digit 3 of the LCD
    */

    // X
    if ((unsigned)buf[6]== 47 && (unsigned)buf[9]== 69) LCD_digit[2]=0;
    if ((unsigned)buf[6]== 31 && (unsigned)buf[9]==101) LCD_digit[2]=1;
    if ((unsigned)buf[6]== 79 && (unsigned)buf[9]==  5) LCD_digit[2]=2;
    if ((unsigned)buf[6]==111 && (unsigned)buf[9]==229) LCD_digit[2]=3;
    if ((unsigned)buf[6]== 95 && (unsigned)buf[9]==165) LCD_digit[2]=4;
    if ((unsigned)buf[6]==239 && (unsigned)buf[9]== 37) LCD_digit[2]=5;
    if ((unsigned)buf[6]==239 && (unsigned)buf[9]== 69) LCD_digit[2]=6;
    if ((unsigned)buf[6]== 31 && (unsigned)buf[9]==229) LCD_digit[2]=7;
    if ((unsigned)buf[6]==111 && (unsigned)buf[9]== 69) LCD_digit[2]=8; 
    if ((unsigned)buf[6]==111 && (unsigned)buf[9]== 37) LCD_digit[2]=9;
	// .X
	if ((unsigned)buf[6]== 47 && (unsigned)buf[9]== 85) { LCD_digit[2]=0; LCD_decimal[2]=1; }
    if ((unsigned)buf[6]== 31 && (unsigned)buf[9]==117) { LCD_digit[2]=1; LCD_decimal[2]=1; }
    if ((unsigned)buf[6]== 79 && (unsigned)buf[9]== 21) { LCD_digit[2]=2; LCD_decimal[2]=1; }
    if ((unsigned)buf[6]==111 && (unsigned)buf[9]==245) { LCD_digit[2]=3; LCD_decimal[2]=1; }
    if ((unsigned)buf[6]== 95 && (unsigned)buf[9]==181) { LCD_digit[2]=4; LCD_decimal[2]=1; }
    if ((unsigned)buf[6]==239 && (unsigned)buf[9]== 53) { LCD_digit[2]=5; LCD_decimal[2]=1; }
    if ((unsigned)buf[6]==239 && (unsigned)buf[9]== 85) { LCD_digit[2]=6; LCD_decimal[2]=1; }
    if ((unsigned)buf[6]== 31 && (unsigned)buf[9]==245) { LCD_digit[2]=7; LCD_decimal[2]=1; }
    if ((unsigned)buf[6]==111 && (unsigned)buf[9]== 85) { LCD_digit[2]=8; LCD_decimal[2]=1; } 
    if ((unsigned)buf[6]==111 && (unsigned)buf[9]== 53) { LCD_digit[2]=9; LCD_decimal[2]=1; }

    // LCD_digit[1]  
  
    /*          .
    LCD b5	b7  b7
	0   54  84  100
    1   38	116 132
    2   86	20  36
    3   118	244 4
    4   102	180 196
    5   246	52  68
    6   246	84  100
    7   38	244 4
    8   118	84  100
    9   118	52  68
    */

    // X
    if ((unsigned)buf[5]== 54 && (unsigned)buf[7]== 84) LCD_digit[1] =0;
    if ((unsigned)buf[5]== 38 && (unsigned)buf[7]==116) LCD_digit[1] =1;
    if ((unsigned)buf[5]== 86 && (unsigned)buf[7]== 20) LCD_digit[1] =2;
    if ((unsigned)buf[5]==118 && (unsigned)buf[7]==244) LCD_digit[1] =3;
    if ((unsigned)buf[5]==102 && (unsigned)buf[7]==180) LCD_digit[1] =4;
    if ((unsigned)buf[5]==246 && (unsigned)buf[7]== 52) LCD_digit[1] =5;
    if ((unsigned)buf[5]==246 && (unsigned)buf[7]== 84) LCD_digit[1] =6;
    if ((unsigned)buf[5]== 38 && (unsigned)buf[7]==244) LCD_digit[1] =7;
    if ((unsigned)buf[5]==118 && (unsigned)buf[7]== 84) LCD_digit[1] =8;
    if ((unsigned)buf[5]==118 && (unsigned)buf[7]== 52) LCD_digit[1] =9;
	// .X
	if ((unsigned)buf[5]== 54 && (unsigned)buf[7]==100) { LCD_digit[1] =0; LCD_decimal[1]=1; }
    if ((unsigned)buf[5]== 38 && (unsigned)buf[7]==132) { LCD_digit[1] =1; LCD_decimal[1]=1; }
    if ((unsigned)buf[5]== 86 && (unsigned)buf[7]== 36) { LCD_digit[1] =2; LCD_decimal[1]=1; }
    if ((unsigned)buf[5]==118 && (unsigned)buf[7]==  4) { LCD_digit[1] =3; LCD_decimal[1]=1; }
    if ((unsigned)buf[5]==102 && (unsigned)buf[7]==196) { LCD_digit[1] =4; LCD_decimal[1]=1; }
    if ((unsigned)buf[5]==246 && (unsigned)buf[7]== 68) { LCD_digit[1] =5; LCD_decimal[1]=1; }
    if ((unsigned)buf[5]==246 && (unsigned)buf[7]==100) { LCD_digit[1] =6; LCD_decimal[1]=1; }
    if ((unsigned)buf[5]== 38 && (unsigned)buf[7]==  4) { LCD_digit[1] =7; LCD_decimal[1]=1; }
    if ((unsigned)buf[5]==118 && (unsigned)buf[7]==100) { LCD_digit[1] =8; LCD_decimal[1]=1; }
    if ((unsigned)buf[5]==118 && (unsigned)buf[7]== 68) { LCD_digit[1] =9; LCD_decimal[1]=1; }
	//
    if ((unsigned)buf[5]==150 && (unsigned)buf[7]==228) LCD_OL = 1; // O.L Overload
    if ((unsigned)buf[5]==150 && (unsigned)buf[7]==212) LCD_OL = 1; // O.L Overload

    // LCD_digit[0] least significant digit
 
    /*          .
    LCD b0  b2  b0
    0	75	29  91
    1	107	13  123
    2	11	61  27
    3	235	93  251
    4	171	77  187
    5	43	221 59
    6	75	221 91
    7	235	13  251
    8	75	93  91
    9	43	93  59
    */

    // X
    if ((unsigned)buf[0]== 75 && (unsigned)buf[2]== 29) LCD_digit[0]=0;
    if ((unsigned)buf[0]==107 && (unsigned)buf[2]== 13) LCD_digit[0]=1;
    if ((unsigned)buf[0]== 11 && (unsigned)buf[2]== 61) LCD_digit[0]=2;
    if ((unsigned)buf[0]==235 && (unsigned)buf[2]== 93) LCD_digit[0]=3;
    if ((unsigned)buf[0]==171 && (unsigned)buf[2]== 77) LCD_digit[0]=4;
    if ((unsigned)buf[0]== 43 && (unsigned)buf[2]==221) LCD_digit[0]=5;
    if ((unsigned)buf[0]== 75 && (unsigned)buf[2]==221) LCD_digit[0]=6;
    if ((unsigned)buf[0]==235 && (unsigned)buf[2]== 13) LCD_digit[0]=7;
    if ((unsigned)buf[0]== 75 && (unsigned)buf[2]== 93) LCD_digit[0]=8;
    if ((unsigned)buf[0]== 43 && (unsigned)buf[2]== 93) LCD_digit[0]=9;
    // .X
	if ((unsigned)buf[0]== 91 && (unsigned)buf[2]== 29) { LCD_digit[0]=0; LCD_decimal[0]=1; }
    if ((unsigned)buf[0]==123 && (unsigned)buf[2]== 13) { LCD_digit[0]=1; LCD_decimal[0]=1; }
    if ((unsigned)buf[0]== 27 && (unsigned)buf[2]== 61) { LCD_digit[0]=2; LCD_decimal[0]=1; }
    if ((unsigned)buf[0]==251 && (unsigned)buf[2]== 93) { LCD_digit[0]=3; LCD_decimal[0]=1; }
    if ((unsigned)buf[0]==187 && (unsigned)buf[2]== 77) { LCD_digit[0]=4; LCD_decimal[0]=1; }
    if ((unsigned)buf[0]== 59 && (unsigned)buf[2]==221) { LCD_digit[0]=5; LCD_decimal[0]=1; }
    if ((unsigned)buf[0]== 91 && (unsigned)buf[2]==221) { LCD_digit[0]=6; LCD_decimal[0]=1; }
    if ((unsigned)buf[0]==251 && (unsigned)buf[2]== 13) { LCD_digit[0]=7; LCD_decimal[0]=1; }
    if ((unsigned)buf[0]== 91 && (unsigned)buf[2]== 93) { LCD_digit[0]=8; LCD_decimal[0]=1; }
    if ((unsigned)buf[0]== 59 && (unsigned)buf[2]== 93) { LCD_digit[0]=9; LCD_decimal[0]=1; }

	// LCD annunciators

	//         M            Hz
	//      DC REL          V  
	//      AC HOLD m oC u  A  
	// buf  1  4    8 11 12 13

	if ((unsigned)(buf[ 1]& 16)) LCD_DC   = 1; // DC
	if ((unsigned)buf[13]== 140) LCD_V    = 1; // Volts
	if ((unsigned)buf[13]== 124) LCD_A    = 1; // Amps
	if ((unsigned)buf[ 8]== 134) LCD_m    = 1; // m - milliamps
	if ((unsigned)buf[12]== 126) LCD_u    = 1; // u - microamps
	if ((unsigned)buf[ 4]== 177) LCD_REL  = 1; // REL
	if ((unsigned)buf[ 4]== 241) LCD_HOLD = 1; // HOLD
	if ((unsigned)buf[13]== 172) LCD_Hz   = 1; // Hz
	if ((unsigned)buf[11]== 191) LCD_oC   = 1; // degrees C
	if ((unsigned)buf[ 4]== 145) LCD_M    = 1; // Mega - ohms

	// printf DMM output to display .. only print leading decimal-point!

	if (LCD_OL==1) { // overload
 	  printf(" 0L - DMM Overloaded, change range setting"); 
	}
	else { // normal operation
     (LCD_minus==1)      ? printf("-"):printf(" ");
      printf("%d",LCD_digit[3]);
      //printf(" [%d,%d] ",buf[3],buf[10]); // digit 3

      (LCD_decimal[2]==1) ? printf("."):printf("");
      printf("%d",LCD_digit[2]);
	  //printf(" [%d,%d] ",buf[6],buf[ 9]); // digit 2

      (LCD_decimal[1]==1) ? printf("."):printf("");
      printf("%d",LCD_digit[1]);
	  //printf(" [%d,%d] ",buf[5],buf[ 7]); // digit 1

      (LCD_decimal[0]==1) ? printf("."):printf("");
      printf("%d",LCD_digit[0]);
	  //printf(" [%d,%d] ",buf[0],buf[ 2]); // digit 0
	}

	printf(" ");
    if (LCD_m)    printf("m"); // m - milliamps
    if (LCD_u)    printf("u"); // u - microamps
    if (LCD_V)    printf("V"); // Volts
    if (LCD_A)    printf("A"); // Amps
    if (LCD_oC)   printf("oC"); // degrees C
    if (LCD_M)    printf("Mohms"); // Mega - ohms
    if (LCD_Hz)   printf("Hz"); // Hz

	printf(" ");
 	if ((LCD_V || LCD_A)) (LCD_DC==1) ? printf("DC") : printf("AC"); // AC/DC

	printf(" ");
    if (LCD_REL)  printf("REL"); // REL
    if (LCD_HOLD) printf("HOLD"); // HOLD

	//printf(" buf[%d,%d,%d,%d,%d,%d,%d,%d] ",buf[1],buf[4],buf[8],buf[11],buf[12],buf[13],buf[14],buf[15]); // ?

	return DMM;
  }

} // decode_Victor86B()


void display_buffer(unsigned char* buf) {
  printf("\n");  
  printf("%d,%d, ",buf[3],buf[10]); // digit 3
  printf("%d,%d, ",buf[6],buf[ 9]); // digit 2
  printf("%d,%d, ",buf[5],buf[ 7]); // digit 1
  printf("%d,%d, ",buf[0],buf[ 2]); // digit 0
  printf("%d,%d, ",buf[1],buf[ 4]); // ?
  printf("\n");
} // display_buffer()


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

	int res;
	unsigned char buf[256];
	hid_device *handle;
	int read=0;
	clock_t ticks1, ticks2;


	//report_HIDs(); // report attached USB HIDs to screen

	handle = hid_open(0x1244, 0xd237, NULL);
	if (!handle) {
	  printf("Victor86B DMM not found - \n  PLUG in USB cable and ensure yellow RS232 button on DMM is set\n");
 	  //return 1;
	}
	else {
	  printf("found Victor86B DMM\n");
	}

	// while (read<10) {
	while (1) {  // read out data from DMM over USB
	  
	  ticks1=clock(); // start-time

	  res = 0;
	  while (res == 0) {
	    res = hid_read(handle, buf, sizeof(buf));
	    if (res == 0) printf("waiting...\n");
	    if (res  < 0) printf("Unable to read()\n");
	  }

	  // measure time take to read data from DMM

	  ticks2=clock()-ticks1; // elapsed time
	  //printf("%ld,",ticks2);
	  //printf("%2.2fsec,",(double)CLOCKS_PER_SEC/(double)ticks2);

	  decode_Victor86B(buf);	  
	  printf("\n");
      read++;

	} // while()

    while (1); //

	hid_close(handle);
	hid_exit();
	 
	return 0;

} // main()


// Victor86B_usbDMM.cpp

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Chief Technology Officer Movidius
Ireland Ireland
David Moloney holds a B.Eng. degree from Dublin City University, and Ph.D. in Engineering from Trinity College Dublin. For the past 25 years he has worked in microelectronics starting in 1985 with Infineon in Munich and ST Microelectronics in Milan, before returning to Ireland 1994 to help found a series of start-up technology companies including Parthus-CEVA and Silansys. David is currently co-founder (2005) and CTO of Movidius Ltd., a fabless semiconductor company headquartered in Dublin and focused on the design of software programmable multimedia accelerator SoCs. He holds 18 US patents and numerous conference and journal papers on DSP and computer architecture. David is a member of the IEEE.

Comments and Discussions