;***************************************************************** ;* Command to receive and decode DT-4000ZC multimeter data * ; Display on LCD and echo on serial line * ;* Hardware: multimeter output has a photocoupler switch * ;* do not use the original serial cable (a cheap hack) * ;* for RS-232 levels connect to SN75150 or equivalent * ;* Used subroutines: * ;* GetSerialByteDa * ;* returns char in ACC if serial buffer was not empty, C=1 * ;* else C=0 * ;* can use another UART if builtin reserved for monitor * ;* serial port settings: 2400/8N1 * ;* SendSerialByte, SendLCDCommand, SendLCDText * ;* as in SBCMon * ;***************************************************************** StringBuffer EQU 8000h ;in XRAM ValBuffer EQU 70h ;in IRAM CmdDMM: MOV DPTR,#StringBuffer MOV Scratch,#010h CmdDMMLoop1: LCALL GetSerialByteDa JNC CmdDMMLoop1 ;loop while serial buffer empty MOV R0,A ;backup received byte ANL A,#0F0h CJNE A,Scratch,CmdDMM ;check correct byte sequence 1x 2x ... Ex MOV A,R0 ;restore received byte MOVX @DPTR,A ;copy byte to XRAM INC DPTR MOV A,Scratch ;inc high nibble of sequence mask SWAP A INC A SWAP A MOV Scratch,A CJNE A,#0F0h,CmdDMMLoop1 ;loop until sequence mask was Ex MOV R0,#ValBuffer ;fill decode buffer with spaces... MOV R1,#16 MOV A,#' ' DMMLoop2: MOV @R0,A INC R0 DJNZ R1,DMMLoop2 LCALL DMMDecode ;decode 7-segment data to ascii buffer MOV C,EA MOV F0,C CLR EA MOV A,#80h LCALL SendLCDCommand ;LCD first line MOV R0,#ValBuffer ;send ascii buffer over serial line MOV R1,#16 DMMLoop3: MOV A,@R0 INC R0 LCALL SendLCDText LCALL SendSerialByte DJNZ R1,DMMLoop3 MOV C,F0 MOV EA,C LCALL SendSerial ;send cursor up to overwrite previous line DB CR,ESC,"[A",0 JNB RI,CmdDMM ;loop if nothing from console MOV A,SBUF CLR RI CJNE A,#CTRLX,CmdDMM ;loop if no CTRL-X detected MOV A,#CR LCALL SendSerialByte LJMP SBCMain DMMDecode: CLR A ;start with R0 = 0 (j) MOV R0,A MOV R4,A ;R4=0 (dot) DMMDLoop: MOV A,R0 ADD A,R0 INC A MOV DPTR,#StringBuffer ADD A,DPL ;add 2j+1 to DPTR JNC DMMDLoop1 INC DPH DMMDLoop1: MOV DPL,A MOVX A,@DPTR MOV R2,A ;backup inputbuffer[2j+1] INC DPTR MOVX A,@DPTR MOV R3,A ;backup inputbuffer[2j+2] MOV A,R0 JZ DMMDLoop2 ;skip decimal dot detection before first digit MOV A,R2 ;detect decimal dot JNB ACC.3,DMMDLoop2 ;go ahead if no decimal dot detected MOV A,#ValBuffer ;write '.' at position j+1 ADD A,R0 INC A MOV R1,A MOV @R1,#'.' MOV R4,#1 ;dot=1 DMMDLoop2: MOV A,R3 ANL A,#15 MOV R3,A ;abcd segments MOV DPTR,#equiv7 MOV A,R2 ;efg segments ANL A,#7 RL A RL A RL A RL A ADD A,R3 MOVC A,@A+DPTR ;read from table MOV B,A ;backup decoded char MOV A,#ValBuffer ADD A,R0 INC A ADD A,R4 MOV R1,A ;position = j+1+dot MOV A,B ;restore decoded char MOV @R1,A ;write decoded char at position j+1+dot CJNE A,#'L',DMMDLoop3 ;go ahead if no overflow symbol DEC R1 MOV A,@R1 CJNE A,#'0',DMMDLoop21 ;replace 0 with O DMMDLoop22: MOV @R1,#'O' SJMP DMMDLoop3 DMMDLoop21: DEC R1 SJMP DMMDLoop22 DMMDLoop3: INC R0 CJNE R0,#4,DMMDLoop MOV DPTR,#StringBuffer+1 ;dot in first digit is the - sign MOVX A,@DPTR JNB ACC.3,DMMD2 MOV ValBuffer,#'-' DMMD2: MOV A,R4 ;write final dot if no dot found JNZ DMMD3 MOV ValBuffer+5,#'.' DMMD3: MOV R0,#1 ;remove leading 0, R0 index (j) DMMDLoop4: MOV A,#ValBuffer ADD A,R0 MOV R1,A CJNE @R1,#'0',DMMD4 ;val[j] INC R1 CJNE @R1,#'.',DMMDLoop41 ;val[j+1] SJMP DMMD4 DMMDLoop41: DEC R1 ;val[j] DEC R1 ;shift digits from position j-1 to j MOV A,@R1 ;val[j-1] INC R1 MOV @R1,A ;val[j] DEC R1 ;val[j-1] MOV @R1,#' ' ;write space INC R0 SJMP DMMDLoop4 DMMD4: MOV DPTR,#StringBuffer+12 MOVX A,@DPTR MOV B,A ANL A,#12 ;V or A JZ DMMD5 MOV ValBuffer+8,#'V' MOV A,B JNB ACC.3,DMMD41 MOV ValBuffer+8,#'A' DMMD41: MOV DPTR,#StringBuffer+10 MOVX A,@DPTR JNB ACC.3,DMMD42 MOV ValBuffer+7,#'m' DMMD42: MOV DPTR,#StringBuffer+9 MOVX A,@DPTR JNB ACC.3,DMMD43 MOV ValBuffer+7,#'u' DMMD43: MOV DPTR,#StringBuffer MOVX A,@DPTR MOV B,A JNB ACC.2,DMMD44 MOV ValBuffer+10,#'D' DMMD44: MOV A,B JNB ACC.3,DMMD45 MOV ValBuffer+10,#'D' DMMD45: SJMP DMMD9 DMMD5: MOV DPTR,#StringBuffer+11 MOVX A,@DPTR JNB ACC.2,DMMD6 MOV ValBuffer+8,#'o' MOV ValBuffer+9,#'h' MOV ValBuffer+10,#'m' MOV DPTR,#StringBuffer+10 MOVX A,@DPTR JNB ACC.1,DMMD51 MOV ValBuffer+7,#'M' SJMP DMMD52 DMMD51: MOV DPTR,#StringBuffer+9 MOVX A,@DPTR JNB ACC.1,DMMD52 MOV ValBuffer+7,#'k' DMMD52: SJMP DMMD9 DMMD6: MOV DPTR,#StringBuffer+12 MOVX A,@DPTR JNB ACC.1,DMMD7 MOV ValBuffer+8,#'H' MOV ValBuffer+9,#'z' MOV DPTR,#StringBuffer+10 MOVX A,@DPTR JNB ACC.1,DMMD61 MOV ValBuffer+7,#'M' SJMP DMMD62 DMMD61: MOV DPTR,#StringBuffer+9 MOVX A,@DPTR JNB ACC.1,DMMD62 MOV ValBuffer+7,#'k' DMMD62: SJMP DMMD9 DMMD7: MOV DPTR,#StringBuffer+11 MOVX A,@DPTR JNB ACC.3,DMMD8 MOV ValBuffer+8,#'n' MOV ValBuffer+9,#'F' SJMP DMMD9 DMMD8: MOV DPTR,#StringBuffer+13 MOVX A,@DPTR JNB ACC.2,DMMD9 MOV ValBuffer+8,#'C' DMMD9: MOV DPTR,#StringBuffer MOVX A,@DPTR JNB ACC.1,DMMD10 MOV ValBuffer+12,#'a' SJMP DMMD11 DMMD10: MOV ValBuffer+12,#'m' DMMD11: MOV DPTR,#StringBuffer+11 MOVX A,@DPTR ANL A,#3 ADD A,#'0' MOV R1,#ValBuffer+13 MOV @R1,A CLR ValBuffer+15 ;null-terminated RET equiv7: DB " ####1##########" DB "#####7#########3" DB "#######4########" DB "##############59" DB "################" DB "###########2####" DB "########L#######" DB "#############068" END Translated from the following C program: http://studenti.fisica.unifi.it/~carla/software/marsh-0.98/marsh.c /* * marsh: a short program to read and save data from * MAS-345 and Digitek-4000 multimeters to a file. * * Copyright (C) 2005 Marcello Carla' * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Address of the author: * Department of Physics, University of Florence * Via G. Sansone 1 * Sesto F.no (Firenze) I 50019 - Italy * e-mail: carla@fi.infn.it * */ /* * Compile with "gcc marsh.c -o marsh" or "make marsh" and run "./marsh". * Use "./marsh -h" to see available options. * */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_PORTS 4 /* can be increased up to 32 */ #define DEFAULT_DEVICE mas345 #define DEFAULT_MODEL "MAS-345" #define DEFAULT_PORT "/dev/ttyS0" #define DEFAULT_TIMEOUT 2.0 /* time to complete measurements (sec) */ #define MAX_TIME_OUT 7 /* disable calculations after these many failures */ #define DATA_STRING 15 /* max length of input data string */ #define MAX_RESULT 40 /* max length for result string */ #define DTR 2 #define RTS 4 struct port_area { char *port; /* port name */ char *model; /* device name */ void (**actions)(); /* device specific routines */ int fd; /* file descriptor */ struct termios oldtio; /* port setting as found at start */ unsigned char inbuf[DATA_STRING+1]; /* data area for input string */ int j; /* character count in inbuf */ int mask; /* flag position in mask */ int position,length; /* position and length of datum in inbuf */ struct timeval ta,tb; /* time structures for diagnostics */ }; struct termios newtio; int rts = RTS, dtr = DTR; int D = 0, DD = 0, DDD = 0; /* verbose debug flag */ char *default_model = DEFAULT_MODEL; char *default_port = DEFAULT_PORT; /* * restore_port_settings - to be called on exit for clean operation */ void restore_port_settings (int exit_value, struct port_area *p) { if (D) printf ("D - restoring port settings at fd %d with %o %o %o %o\n", p->fd, p->oldtio.c_iflag, p->oldtio.c_cflag, p->oldtio.c_oflag, p->oldtio.c_lflag); ioctl (p->fd, TIOCMBIC, &dtr); /* clear the DTR line */ tcflush(p->fd, TCIFLUSH); if (tcsetattr(p->fd, TCSANOW, &p->oldtio)) perror ("### 'restore port settings' failed"); close (p->fd); } /* * catch_the_signal(signal) - termination signals are to be * catched for clean exit - SIGALRM has * to be catched to return from pause() */ void catch_the_signal (signal) { if (signal == SIGALRM || signal == SIGCHLD) { if (DD) printf ("DD - received signal %d\n", signal); return; } if (D) printf ("D - received signal %d\n", signal); fcloseall(); /* close all streams, mainly the data to file */ exit (0); } /* * close the auxiliary calculator */ void close_calculator (int exit_value, int fd_elab[]) { write (fd_elab[1], "quit\n", 5); close (fd_elab[1]); close (fd_elab[2]); } /* * time operations: difference and sum */ /* return time difference as a timeval structure */ void time_diff (struct timeval *a, struct timeval *b, struct timeval *d) { d->tv_sec = a->tv_sec - b->tv_sec; d->tv_usec = a->tv_usec - b->tv_usec; if (d->tv_usec < 0) { d->tv_usec += 1000000; d->tv_sec--; } } /* return time sum as a timeval structure */ void time_add (struct timeval *a, struct timeval *b, struct timeval *s) { s->tv_sec = a->tv_sec + b->tv_sec; s->tv_usec = a->tv_usec + b->tv_usec; if (s->tv_usec > 999999) { s->tv_usec -= 1000000; s->tv_sec++; } } /* return time difference in millisec as a float quantity */ float delta_time (struct timeval *a, struct timeval *b) { return ((a->tv_sec - b->tv_sec)*1000. + (a->tv_usec - b->tv_usec)/1000.); } /* convert a float time (in sec) to a timeval structure */ void time_to_timeval (float t, struct timeval *ts) { ts->tv_sec = t; ts->tv_usec = (t - ts->tv_sec)*1e6; } /* * time_stamp - convert timeval to a char string with format: * * yyyy-mm-dd hh:mm:ss.ddz * 1 1 2 * 0 5 0 5 0 * * and return the string and its length (23 chars + trailing \0) * */ int time_stamp(char *buf, struct timeval *tv) { struct tm now; localtime_r(&tv->tv_sec, &now); sprintf (buf,"%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d.%2.2d ", now.tm_year+1900, now.tm_mon+1, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, (int) (tv->tv_usec/1.e4)); buf[23] = '\0'; return (23); } /* * open and setup the port */ void open_setup_line (struct port_area *p) { if (D) printf ("D - entering line setup for port <%s>\n", p->port); if ((p->fd = open (p->port, O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1) { fprintf (stderr, "### Unable to open port %s: %s\n", p->port, strerror(errno)); exit (-1); } if (D) printf ("D - port <%s> opened with file descriptor %d\n", p->port, p->fd); /* save old port settings and set up new attributes */ /* ref: man 3 termios */ if (tcgetattr (p->fd, &p->oldtio)) { fprintf (stderr, "### Failed reading setting of port <%s>: %s\n", p->port, strerror(errno)); exit (-1); } if (D) printf("D - found port settings %o %o %o %o on port <%s>\n", p->oldtio.c_iflag, p->oldtio.c_cflag, p->oldtio.c_oflag, p->oldtio.c_lflag, p->port); /* register to restore old port settings on exit */ if (on_exit((void*) restore_port_settings, p)) { perror ("### Cannot register for clean exit"); exit (-1); } /* now activate new settings and flush buffers */ if (tcsetattr(p->fd, TCSANOW, &newtio)) { fprintf (stderr, "### Cannot set port attributes on port <%s>: %s\n", p->port, strerror(errno)); exit (-1); } if (D) printf("D - done with line setup\n"); return; } /* * start the calculator */ int start_calc (char * elab_p, int fd_elab[]) { int pid_comp; if (D) printf ("D - starting external calculator\n"); if (pipe(fd_elab) || pipe(fd_elab+2)) { perror ("### Failed creating pipes for calculator. Calculation disabled"); return (-1); } if (D) printf ("D - created pipes (%d %d),(%d %d)\n", fd_elab[0], fd_elab[1], fd_elab[2], fd_elab[3]); if ((pid_comp = fork()) == -1) { perror ("### 'fork' failed - calculation disabled"); close (fd_elab[0]); close (fd_elab[1]); close (fd_elab[2]); close (fd_elab[3]); return (-1); } if (D) printf ("D - forked. Here pid %d\n", pid_comp); if (pid_comp == 0) { /* this is child - 'exec bc' */ close (fd_elab[1]); close (fd_elab[2]); if (D) printf ("D - child - starting calculator 'bc' with i/o %d %d\n", fd_elab[0], fd_elab[3]); if (dup2(fd_elab[0],0) == -1 || dup2(fd_elab[3],1) == -1 || execlp ("bc", "bc", 0) == -1) { perror ("### Couldn't start external calculator."); close (fd_elab[0]); close (fd_elab[3]); exit (-1); } } /* this is parent */ close (fd_elab[0]); close (fd_elab[3]); if (on_exit((void *) close_calculator, fd_elab)) { perror ("### Failed registering exit routine for calculator program"); fprintf (stderr, "### Maybe you shall kill it yourself.\n"); } if (D) printf ("D - parent - sending preset command to calculator: <%s>\n", elab_p); if (elab_p) { /* send preset to calculator, if any */ write (fd_elab[1], elab_p, strlen(elab_p)); write (fd_elab[1], "\n", 1); } return (0); } /* * on line elaboration of data */ int local_elab (char **elab, struct port_area *p, int last_port, char * result, int fd_elab[]) { int j, nread, sel; char buf[20]; fd_set pipe_fd; struct timeval t_calc; static int timeouts = MAX_TIME_OUT; /* send data to calculator: m0=;m1=; etc. */ for (j = 0 ; j <= last_port ; j++) { sprintf (buf, "m%d=%*.*s;", j, (p+j)->length, (p+j)->length, (p+j)->inbuf+(p+j)->position); if (DDD) printf ("DDD - sending to child: <%s>\n", buf); write (fd_elab[1], buf, strlen(buf)); } /* send command */ write (fd_elab[1], *elab, strlen(*elab)); write (fd_elab[1], "\n", 1); if (DD) printf ("DD - going to read from child ...\n"); /* wait for results (up to 0.1 sec) */ FD_ZERO (&pipe_fd); FD_SET (fd_elab[2], &pipe_fd); t_calc.tv_sec = 0; t_calc.tv_usec = 100000; usleep(10000); sel = select(fd_elab[2]+1, &pipe_fd, NULL, NULL, &t_calc); if (sel > 0) { nread = read (fd_elab[2], result, 40); if (nread == MAX_RESULT) { fprintf (stderr, "### %s\n", "Result string is too long. Calculations disabled!"); nread = 0; *elab = 0; } if (nread > 0) { for (j = 0; j < nread ; j++) { /* sanity check for result string */ if (! isprint(result[j])) { if (result[j] != '\n') fprintf (stderr, "### Bad character from calculator: %o at %d\n", result[j], j); result[j] = ' '; } } } else if (nread == -1) { perror ("### Failed reading results from calculator"); nread = 0; } } else if (sel == 0) { fprintf (stderr, "### Timeout while reading from calculator\n"); nread = 0; if (timeouts-- <= 0) { fprintf (stderr, "### Too many timeouts. Calculation disabled!\n"); *elab = 0; } } else { perror ("### Failed waiting calculator results. Calculation disabled!"); *elab = 0; nread = 0; } result[nread] = '\0'; if (DD) printf ("DD - received: %d <%s>\n", nread, result); return nread; } /* * MAS-345 */ void mas345_open (struct port_area *p) { bzero (&newtio, sizeof(newtio)); /* setup termios structure */ newtio.c_cflag = CS7 | CSTOPB | CLOCAL | CREAD; newtio.c_cc[VMIN] = 14; cfsetospeed (&newtio, B600); cfsetispeed (&newtio, B600); open_setup_line (p); if (D) printf("D - going to clear RTS and set DTR\n"); ioctl (p->fd, TIOCMBIC, &rts); /* clear RTS line for - supply */ ioctl (p->fd, TIOCMBIS, &dtr); /* set DTR line for + supply */ usleep(1000); tcflush(p->fd, TCIOFLUSH); p->position = 3; /* postion and length of datum in input buffer */ p->length = 6; } void mas345_start (struct port_area *p) { tcflush(p->fd, TCIOFLUSH); write (p->fd, "\n", 1); gettimeofday (&p->ta, NULL); } void mas345_read (struct port_area *p, int *mask) { #define STRING 14 int j; /* ... check for unwanted characters ...*/ if (p->mask & ~mask[0]) { tcflush (p->fd, TCIOFLUSH); fprintf (stderr, "### Spurious characters from device %s on line %s - flushed!\n", p->model, p->port); return; } p->j += read (p->fd, p->inbuf + p->j, STRING - p->j); /* ... and read */ gettimeofday (&p->tb, NULL); if (DDD) printf ("DDD - port %s byte %d: %o\n", p->port, p->j, *(p->inbuf + p->j - 1)); if (p->j == STRING) { /* if string is complete check for sanity */ mask[0] &= ~p->mask; /* let's suppose everything is o.k. */ mask[1] &= ~p->mask; for (j = 0; j < p->j - 1 ; j++) { /* check characters? */ if (! isprint(p->inbuf[j])) { fprintf (stderr, "### Bad character from device %s on line %s: %o at %d\n", p->model, p->port, p->inbuf[j], j); p->inbuf[j] = ' '; mask[1] |= p->mask; /* data are not valid */ } } if (p->inbuf[p->j-1] == '\r') { /* check terminator? */ p->j--; } else { fprintf (stderr, "### Bad terminator from device %s on line %s: %o\n", p->model, p->port, p->inbuf[p->j-1]); mask[1] |= p->mask; } if (!strncmp(p->inbuf+5, ".OL", 3) || /* overflow? */ !strncmp(p->inbuf+5, "O.L", 3) || !strncmp(p->inbuf+5, "OL.", 3)) { fprintf (stderr, "### Overflow in device %s on line %s\n", p->model, p->port); mask[1] |= p->mask; } } } void (*mas345[])() = {mas345_open, mas345_start, mas345_read}; /* * Digitek 4000 */ void digitek_open (struct port_area *p) { bzero (&newtio, sizeof(newtio)); /* setup termios structure */ newtio.c_cflag = CS8 | CLOCAL | CREAD; newtio.c_cc[VMIN] = 1; /* Digitek-4000 is not reliable enough to allow reading the full string in a single operation */ cfsetospeed (&newtio, B2400); cfsetispeed (&newtio, B2400); open_setup_line (p); if (D) printf("D - going to clear RTS and set DTR\n"); ioctl (p->fd, TIOCMBIC, &rts); /* clear RTS line */ ioctl (p->fd, TIOCMBIC, &dtr); /* clear DTR line */ usleep(1000); tcflush(p->fd, TCIOFLUSH); p->position = 0; /* postion and length of datum in input buffer */ p->length = 6; } void digitek_start (struct port_area *p) { tcflush(p->fd, TCIOFLUSH); ioctl (p->fd, TIOCMBIS, &dtr); /* set DTR line to start operation */ gettimeofday (&p->ta, NULL); } void digitek_read (struct port_area *p, int *mask) { #define STRING 14 /* each byte from Digitek-4000 contains a sequence code in upper four bits */ unsigned char sequence[] = {0020, 0040, 0060, 0100, 0120, 0140, 0160, 0200, 0220, 0240, 0260, 0300, 0320, 0340}; /* look-up table for 7 segments to bcd conversion */ char equiv[8][16] = {" ####1##########","#####7#########3", "#######4########","##############59", "################","###########2####", "########L#######","#############068"}; int j, efg, abcd, dot=0; char value[16] = " "; if (p->mask & ~mask[0]) { tcflush (p->fd, TCIOFLUSH); fprintf (stderr, "### Spurious characters from device %s on line %s - ignored\n", p->model, p->port); return; } read (p->fd, &p->inbuf[p->j], 1); gettimeofday (&p->tb, NULL); if ((p->inbuf[p->j] & 0360) == sequence[p->j]) { p->j++; } else { p->j = 0; } if (p->j == STRING) { ioctl (p->fd, TIOCMBIC, &dtr); /* stop transmission */ mask[0] &= ~p->mask; mask[1] &= ~p->mask; /* input string is complete; translate filling buffer "value" */ /* bytes 1-2, 3-4, 5-6 and 7-8 code for one digit+dot each pair */ for (j = 0; j < 4 ; j++) { /* process digits */ if (j && p->inbuf[j+j+1] & 8) { /* decimal dot here? */ value[j+1] = '.'; dot = 1; } efg = p->inbuf[j+j+1] & 7; /* 3 out of 7 segments */ abcd = p->inbuf[j+j+2] & 15; /* 4 out of 7 segments */ /* pick character 'abcd' in string 'efg' in table 'equiv' */ value[j+1+dot] = equiv[efg][abcd]; if (value[j+1+dot] == 'L') { /* 0L -> OL in overflow */ if (value[j+dot] == '0') value[j+dot] = 'O'; else value[j+dot-1] = 'O'; mask[1] |= p->mask; /* data are not valid */ } } /* dot in first digit is the - sign */ if (p->inbuf[1] & 8) value[0] = '-'; if (dot == 0) value[5] = '.'; /* no dot -> final dot */ j = 1; while (value[j] == '0' && value[j+1] != '.') { /* remove leading zero's */ value[j] = value[j-1]; value[j-1] = ' '; j++; } if (p->inbuf[12] & 12) { /* Volt or Ampere */ value[8] = 'V'; /* maybe Volt */ if (p->inbuf[12] & 8) value[8] = 'A'; /* Ampere */ if (p->inbuf[10] & 8) value[7] = 'm'; /* milli */ if (p->inbuf[9] & 8) value[7] = 'u'; /* micro */ if (p->inbuf[0] & 4) value[10] = 'D'; /* AC/DC */ if (p->inbuf[0] & 8) value[10] = 'A'; value[11] = 'C'; } else if (p->inbuf[11] & 4) { /* ohm */ memcpy (value+8, "ohm", 3); if (p->inbuf[10] & 2) value[7] = 'M'; /* Mega */ if (p->inbuf[9] & 2) value[7] = 'k'; /* kilo */ } else if (p->inbuf[12] & 2) { /* Hz */ value[8] = 'H'; value[9] = 'z'; if (p->inbuf[10] & 2) value[7] = 'M'; /* Mega */ if (p->inbuf[9] & 2) value[7] = 'k'; /* kilo */ } else if (p->inbuf[11] & 8) { /* nF */ value[8] = 'n'; value[9] = 'F'; } else if (p->inbuf[13] & 4) { /* degree */ value[8] = 'C'; } if (p->inbuf[0] & 2) value[12] = 'a'; /* autorange */ else value[12] = 'm'; /* manual */ value[13] = '0' + (p->inbuf[11] & 3); /* 1: Hold 2: Relative */ *mask &= ~p->mask; memcpy (p->inbuf, value, 15); p->j = 14; } } void (*digitek[])() = {digitek_open, digitek_start, digitek_read}; /* * "the main" */ int main (int argc, char *argv[]) { struct port_area p[MAX_PORTS]; int last_port = 0, mask[2], mask_prototype = 0; struct sigaction act; int j, l, stamp_s=0, stamp_t=0, file_out=0, file_only=0, sel; int out_fd; FILE *out_stream; int fd_elab[4]={-1,-1,-1,-1}; /* descriptors for pipe to calculator */ fd_set rfds; char c, *out_file, *heading = 0; char *elab = 0, *elab_p = 0; struct timeval request, tb, ts; /* data structures for timing */ struct timeval interval, begin, future, elapsed, now; struct timeval timeout, deadline; char outbuf[40 + MAX_RESULT + MAX_PORTS * (DATA_STRING + 1)]; float delay = 1., value; int repeat = -1, nc; struct itimerval timer; /* printf ("### marsh - Copyright (C) 2005 Marcello Carla' ###\n"); */ p[0].port = default_port; /* preset defaults for first device */ p[0].actions = DEFAULT_DEVICE; p[0].model = default_model; time_to_timeval (DEFAULT_TIMEOUT, &timeout); /* parse command line options */ #define display \ "\nmarsh - get readings from multimeters via RS232 line\n"\ "\n"\ "Usage: marsh [OPTION]\n"\ "\n"\ " -l (string) line to be used (default: /dev/ttyS0)\n"\ " -m (string) one more line for one more multimeter\n"\ " -j (string) model for one more multimeter\n"\ " -i (float) time interval between readings (sec.)\n"\ " -r (int) measure repetition count (default: endless)\n"\ " -s add time stamp\n"\ " -t add elapsed time (sec.)\n"\ " -o (filename) write output to file\n"\ " -f (filename) write output **only** to file\n"\ " -c (string) insert comment line into file head\n"\ " -e (string) on line calculations with 'bc'; readings are m0, m1, ... \n"\ " -p (string) preset string for external calculator\n"\ " -d activate verbose debug (more d's more debug) \n"\ " -z (float) time for multimeter to complete \n"\ " -h print this help and exit\n" opterr = 0; while ((c = getopt (argc, argv, "di:l:m:j:r:hsto:f:c:e:p:z:")) != -1) switch (c) { case 'd': if (D && DD) DDD = 1; if (D) DD = 1; D = 1; if (!DD) printf ("Debug now active!\n"); break; case 'i': if (sscanf(optarg, "%f" , &delay) != 1) { fprintf (stderr, "### Bad argument for time delay: <%s>\n", optarg); exit (-1); } break; case 'l': p[0].port = optarg; if (D) printf ("D - selected port <%s>\n", p[0].port); break; case 'm': if (D) printf ("D - adding one more multimeter on line <%s>\n", optarg); if (++last_port == MAX_PORTS) { fprintf (stderr, "%s%d%s\n", "### Only ", MAX_PORTS, " ports allowed. Modify MAX_PORTS and recompile."); exit (-1); } p[last_port].port = optarg; p[last_port].actions = p[last_port-1].actions; p[last_port].model = p[last_port-1].model; break; case 'j': if (optarg[0] == 'm') { p[last_port].actions = mas345; /* device is a MAS345 */ p[last_port].model = "MAS-345"; } else if (optarg[0] == 'd') { p[last_port].actions = digitek; /* device is a Digitek*/ p[last_port].model = "Digitek-4000"; } else { fprintf (stderr, "Model <%s> is unknown\n", optarg); fprintf (stderr, "Enter m for MAS-345 or d for Digitek\n"); exit (-1); } break; case 'r': repeat = atoi(optarg); if (repeat < 0) { fprintf (stderr, "%s%d%s\n", "### Warning - negative repetition count <", repeat, "> taken as 'endless loop'"); repeat = -1; } if (D) printf("D - repetition count: %d\n", repeat); break; case 's': stamp_s = 1; break; case 't': stamp_t = 1; break; case 'f': file_only = 1; case 'o': file_out = 1; if (optarg) out_file = optarg; if (D) printf ("D - data output to file <%s>\n", out_file); break; case 'c': heading = optarg; break; case 'e': elab = optarg; if (D) printf ("D - process data with: <%s>\n", elab); break; case 'p': elab_p = optarg; break; case 'z': if (sscanf(optarg, "%f", &value) != 1) { fprintf (stderr, "### Bad argument for timeout: <%s>\n", optarg); exit (-1); } time_to_timeval (value, &timeout); break; case 'h': case '?': default: if (optopt != '?') { fprintf (stderr, "### Unknown option: -%c.\n", optopt); fprintf (stderr, display); exit (-1); } fprintf (stderr, display); exit (0); } if (optind != argc) { for (j = optind; j < argc; j++) { fprintf (stderr, "### Unknown argument: <%s>\n", argv[j]); } fprintf (stderr, display); exit (-1); } /* register to catch stop signals and force clean exit; register SIGALRM to return after pause() */ act.sa_handler = catch_the_signal; sigemptyset(&act.sa_mask); /* exclude all signals from block mask */ act.sa_flags = 0; /* behaviour flag: none */ if (sigaction(SIGINT, &act, 0) || sigaction(SIGQUIT, &act, 0) || sigaction(SIGTERM, &act, 0) || sigaction(SIGALRM, &act, 0) || sigaction(SIGHUP, &act, 0) || sigaction(SIGCHLD, &act, 0)) { perror ("### Error while registering signal handler"); exit (-1); } if (D) printf ("D - registered signal handlers - starting calculator\n"); /* start the calculator, if requested */ if (elab) { if (start_calc (elab_p, fd_elab)) { elab = 0; /* failed - disable calculations */ } else { if (D) printf ("D - started calculator with i/o %d %d\n", fd_elab[1], fd_elab[2]); } } /* build the 'interval' structure from 'delay' */ interval.tv_sec = delay; interval.tv_usec = (delay - interval.tv_sec) * 1e6; if (D) printf("D - time delay: %f = %ld sec + %ld usec\n", delay, interval.tv_sec, interval.tv_usec); /* open and setup the lines */ for (l = 0 ; l <= last_port ; l++ ) { /* open lines and set param's */ p[l].actions[0] (&p[l]); mask_prototype |= p[l].mask = 1 << l; } /* open output file (if any) */ if (file_out) { out_fd = open (out_file, O_WRONLY | O_CREAT | O_EXCL, 0644); if (out_fd == -1) { fprintf (stderr, "### Unable to open file <%s>: %s\n", out_file, strerror(errno)); exit (-1); } out_stream = fdopen (out_fd, "w"); /* write a header to the file */ gettimeofday (&begin,NULL); /* time stamp */ nc = time_stamp (outbuf, &begin); fprintf (out_stream, "#\n# %s\n", outbuf); if (heading) fprintf (out_stream, "# %s\n", heading); for (l = 0 ; l <= last_port ; l++ ) /* devices and lines */ fprintf (out_stream, "# <%s> on line <%s>\n", p[l].model, p[l].port); if (elab) fprintf (out_stream, "# -e: <%s>\n", elab); /* bc commands */ if (elab_p) fprintf (out_stream, "# -p: <%s>\n", elab_p); fprintf (out_stream, "#\n"); } /* start the time count */ timer.it_interval.tv_sec = 0; timer.it_interval.tv_usec = 0; gettimeofday (&begin,NULL); future.tv_sec = begin.tv_sec; future.tv_usec = begin.tv_usec; /* main loop - here readings are taken */ while (repeat>0 ? repeat-- : repeat) { /* send a request to each active line and clear the buffers */ mask[0] = mask[1] = mask_prototype; gettimeofday (&request, NULL); time_add (&request, &timeout, &deadline); for (l = 0 ; l <= last_port ; l++ ) { p[l].inbuf[0] = '\0'; p[l].j = 0; p[l].actions[1] (&p[l]); } if (DD) printf ("DD - count %d mask = %o\n", repeat, mask[0]); /* receive from each line or timeout */ while (mask[0]) { FD_ZERO (&rfds); /* prepare the select() data structure */ for (l = 0 ; l <= last_port ; l++ ) FD_SET (p[l].fd, &rfds); gettimeofday (&now, NULL); time_diff (&deadline, &now, &ts); if (ts.tv_sec < 0) { ts.tv_sec = 0; ts.tv_usec = 0; } if ((sel = select (p[last_port].fd+1, &rfds, NULL, NULL, &ts)) > 0) { /* for each port, if ready, call read action to manage input */ for (l = 0 ; l <= last_port ; l++ ) { if (FD_ISSET(p[l].fd, &rfds)) { p[l].actions[2] (&p[l], mask); } } } else if (sel == 0) { fprintf (stderr, "### Time out on input on:\n"); for (l = 0 ; l <= last_port ; l++ ) { if (mask[0] & p[l].mask) { fprintf (stderr, "model %s on line %s after %d bytes\n", p[l].model, p[l].port, p[l].j); } } break; /* time is over - exit from the 'while mask' loop */ } else { perror ("### Unable to select()"); exit (-1); } } if (DD) { for (l = 0 ; l <= last_port ; l++ ) { printf ("DD - line: %d: <%s>\n", l, p[l].inbuf); } } /* write time section to output buffer */ gettimeofday (&tb,NULL); if (mask[1]) sprintf (outbuf, "#"); /* data are not valid */ else sprintf (outbuf, " "); nc = 1; if (stamp_s) nc += time_stamp(outbuf + nc, &request); if (stamp_t) { time_diff(&request, &begin, &elapsed); nc += sprintf (outbuf+nc, "%10.1f ", elapsed.tv_sec+elapsed.tv_usec/1.e6); } if (DD) printf ("DD - stamp:<%d> <%s>\n", nc, outbuf); /* write data section */ for (l = 0 ; l <= last_port ; l++ ) { p[l].inbuf[p[l].j] = '\0'; if (DD) printf("DD - line %d: <%s>\n", l, p[l].inbuf); if ((mask[0] & p[l].mask) == 0) { nc += sprintf (outbuf+nc, " %s", p[l].inbuf); } else nc += sprintf (outbuf+nc, " "); } /* execute on line data elaboration */ if (DD) printf ("DD - starting optional elaboration\n"); if (elab && !mask[1]) { outbuf[nc++] = ' '; outbuf[nc++] = ' '; nc += l = local_elab (&elab, p, last_port, outbuf+nc, fd_elab); if (l == 0 && elab) outbuf[0] = '#'; /* mark for bad record */ } nc += sprintf (outbuf+nc, "\n"); /* write output buffer to console & file */ if (!file_only) printf ("%s", outbuf); if (file_out) fprintf (out_stream, "%s", outbuf); /* schedule next operation */ time_add (&future, &interval, &future); /* when again? */ gettimeofday (&now, NULL); time_diff (&future, &now, &timer.it_value); /* time to wait */ if (DD) printf ("going to wait for %ld sec + %ld usec\n", timer.it_value.tv_sec, timer.it_value.tv_usec); if (timer.it_value.tv_sec < 0) { #define ErrSync "Error: synchronization lost - trying to resynchronize" if (file_out) write (out_fd, "# "ErrSync"\n", sizeof(ErrSync)+3); while (timer.it_value.tv_sec < 0) { if (file_only) fprintf (stderr, "%s\n", ErrSync); else printf ("# %s\n", ErrSync); time_add (&future, &interval, &future); time_diff (&future, &now, &timer.it_value); } } /* now set the timer and go to sleep until next alarm */ if (setitimer(ITIMER_REAL, &timer, NULL)) { perror ("### Couldn't start timer"); exit (-1); } pause(); /* wait for SIGALRM signal */ } exit (0); }