/* dmm -- digital multimeter Micronta (Radio Shack 22-182)
* 
* Description:
* A simple program to read a value from the Micronta (Radio Shack 22-182)
* digital multimeter, DMM, every sec_per_sample seconds. 
* The program dmm is adapted from 
* Radio Shack DMM with RS-232 [dvm]. Output is sent to stdout and 
* to daily datalog file[date].txt. 
* To exit and close open files, press "enter" key 
* and wait for next sample. If dmm seems to hang waiting for data from 
* DMM, press "COM" key on DMM to start communication. After data starts
* press "COM" again to keep from getting corrupted data frames.
*
* Configuration:
* Micronta (Radio Shack 22-182) Digital Multimeter (DMM) connected to 
* Raspberry Pi (RPI) 2 Model B running Rasperian NOOBS (Debian). The DMM 
* is connected to the RPI via a Micro Innovations USB-RS232 adapter.
* 
* To install and run USB-RS232 adapter on RPI,:
*   disconnect USB-RS232 adapter 
*   $ lsusb
*       note: no serial adapter should be listed 
*   connect Micro Innovations USB-RS232 adapter between RPI and DMM.
*   $ lsusb
*       shows Prolific Tech PL2303 Serial
*       note port as bus 001 device 009 ID 067b:2303
*   $ dmesg
* 		note for USB serial idVendor=067b, idProduct-2303, which
* 		confirms vendor and product.
* 	$ sudo modprobe usbserial vendor=0x067b product=0x2303
* 		attaches USB-Serial adapter to linux kernal.
*   $ dmesg
*       note last line: usb 1-1.3: pl2303 converter now attached
*       as ttyUSB0
*   $ cc dvm.c -odmv.exe
*   press "COM" button on DMM to turn on RS-232 frames, 
* 		otherwise, dmm hangs.
*   $ ./dmm.exe /dev/ttyUSB0 >dmmdata.txt
*   After dmm starts sending data and to prevent corrupting frames, 
* 		press "COM" button again to turn off "COM". 
*/

/* example stdout
pi@raspberrypi ~/Documents/rnd/c/dev_c/dmm $ ./dmm.exe /dev/ttyUSB0 5
debug: Seconds per sample: 5
dmm -- digital multimeter Micronta ver: 2016-0816
usage: ./dmm.exe /dev/ttyUSB0 [int sec_per_sample]
Seconds per sample: 5
press 'enter' key to exit
15:47:49, DC-001.4 mV  
15:48:05, DC-001.0 mV  
15:48:10, DC 000.3 mV  
15:48:15, DC 000.2 mV  
15:48:20, DC 000.0 mV  
15:48:25, DC 000.1 mV  
15:48:30, DC-000.0 mV  

15:48:35, DC 000.0 mV  
pi@raspberrypi ~/Documents/rnd/c/dev_c/dmm $ ^C
pi@raspberrypi ~/Documents/rnd/c/dev_c/dmm $ 

*/

/* references
 * [dvm]	http://www.linuxtoys.org/dvm/dvm.html. no copywrite, no license. Code 
 * 			seems to public but without a specific license.
 * [kbhit]	http://cc.byexamples.com/2007/04/08/non-blocking-user-input-in-loop-without-ncurses
 * 			no copywrite, no license. Code 
 * 			seems to public but without a specific license.
 * [time]	https://www.gnu.org/software/libc/manual/html_node/Time-Functions-Example.html
 * 			Copyright 2016 Free Software Foundation, Inc. Verbatim 
 * 			copying and distribution of this entire article is permitted
 * 			in any medium, provided this notice is preserved.
 * [vws]	Virtual Weather Station, http://www.ambientweather.com/software.html
 * [kr2]	Brian Kernigahan and Dennis Richie, "The C Programming 
 * 			Language", 2nd ed., 1988.
 * [mid]	Substring in c programming, 
 * 			http://www.programmingsimplified.com/c/source-code/c-substring. 
 * 			Programming Simplified is licensed under a Creative Commons 
 * 			Attribution-NonCommercial-NoDerivs 3.0 Unported License. You
 * 			are free to: Share — copy and redistribute the material in 
 * 			any medium or format. The licensor cannot revoke these 
 * 			freedoms as long as you follow the license terms.
 * 
 * 
 */ 

/* Change log:
* 2016-0914, Scott Marin, removed unnecessary code. Tried various ideas 
* for FIXME02. Corrected typos in comments.
* 
* 2016-0816, Scott Marin, changed write c to printf c for DMM readings.
* Added date and time stamps [time][kr2] in semi Virtual Weather Station 
* [vsw]	format. Enabled daily datalog file (yymmdd.txt) in comma 
* separated variable (csv) format that LibreOffice Calc reads and plots.
* Incorporated substring function [mid], similar to Basic MID$ function, 
* to break DMM data frame into type, value, and units fields. Added
* optional command line parameter to set sample period, sec_per_sample.
* FIXME02, dmm requires pressing "COM" on DMM to start samples and 
* pressing "COM" again, i.e. "COM" off, to avoid corrupting samples.
* 
* 2016-0815, Scott Marin, Changed name to dmm. Added stdlib.h to resolve 
* implicit declaration warnings. Added kbhit() to do a non-blocking
* test of a keyboard hit and to break the capture loop when an
* "enter" key is pressed [kbhit]. Added tcflush statement to 
* resolve FIXME01 buffer issue. Added sec_per_sample constant 
* and set to 10 sec, which works well for debug and recording. 
* 
* 2016-0813, Scott Marin, adapted from 
* http://www.linuxtoys.org/dvm/dvm.html [dvm]
* for Raspberry Pi (RPI) 2 Model B running Raspbian NOOBS (Debian)
* and using gcc 4.6 compiler. 5ea warnings exit() implicit declaration 
* -- ignored. dvm working.
* FIXME01, buffer issue in that output follows input after many samples. 
*/

#include <stdlib.h>
#include <stdio.h>
#include <termio.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <time.h> /*required for time() function */
#include <string.h> /*added to manipulate DMM frame */

#define SECPERSAMP 10
#define TLEN 150  /*required for time() function */
#define FRAMELENGTH 20
#define KBSTRLEN 20
#define VERSION "2016-0914"

/*for substring function. See [mid]*/
void substring(char [], char[], int, int);

int main (int argc, char *argv[])
{
    int sec_per_sample=SECPERSAMP;
    int i; /*index for frame character*/
//    int in; 
//	int j = 0; /*index for debugging date change*/ /*no longer used*/
	int m; /* modem control lines */
	int fd_dev; /* FD for the open serial port */
	char c; /* char received from the DMM */
    char frame[FRAMELENGTH]=""; /*frame of data from DMM*/
    char frtype[FRAMELENGTH]=""; /* DC | AC | blank */
    char frvalue[FRAMELENGTH]=""; /*e.g. -0.000 */
    char frunits[FRAMELENGTH]=""; /*e.g. mV*/
    //char framecat[FRAMELENGTH]="";		/*no longer used */
    char datebuf[TLEN] ="";  /*used with time() function */
	char timebuf[TLEN] ="";  /*used with time() function */
	time_t curtime;      /*required for time() function */
	struct tm *loc_time; /*required for time() function */
	
	struct termios tbuf; /* serial line settings */
	char kbstr[KBSTRLEN]; /*keyboard string*/
	FILE *datafilep;
	
	if ((argc !=2) && (argc !=3)) {
		printf("Usage: %s tty_port [sec_per_sample]\n", argv[0]);
		exit(1);
	}
	
	if (( fd_dev = open(argv[1], O_RDWR, 0)) < 0 ) {
		printf("Unable to open tty port specified\n");
		exit(1);
	}

	if (argc >= 3) {
		sec_per_sample = atoi(argv[2]);
		//printf("debug: Seconds per sample: %d\n", sec_per_sample);
	}

	/*print program prompt*/
	printf("dmm -- digital multimeter Micronta 22-182 ver: %s\n",VERSION);
	printf("usage: ./dmm.exe /dev/ttyUSB0 [int sec_per_sample]\n");
	printf("Seconds per sample: %d\n", sec_per_sample);
	printf("press 'enter' key to exit\n");
	
	/* set up the line parameters : 1200,7,2,N */
	tbuf.c_cflag = CS7|CREAD|CSTOPB|B1200|CLOCAL;
	tbuf.c_iflag = IGNBRK;
	tbuf.c_oflag = 0;
	tbuf.c_lflag = 0;
	tbuf.c_cc[VMIN] = 1; /* character-by-character input */
	tbuf.c_cc[VTIME] = 0; /* no delay waiting for characters */
	if (tcsetattr(fd_dev, TCSANOW, &tbuf) < 0) {
		printf("%s: Unable to set device '%s' parameters\n",argv[0], argv[1]);
		exit(1);
	}
	m = TIOCM_DTR;
	if (ioctl(fd_dev, TIOCMSET, &m) < 0) {
		printf("%s: Unable to set '%s' modem status\n",argv[0], argv[1]);
		exit(1);
	}

	//write(fd_dev,"D", 1); /*isn't needed here if sent every frame*/
	/*Activate data frame transmission from DMM to RPI.*/

	while (!kbhit()) {
		
		/*create datelog filename in [vws], yymmdd.txt*/
		/*code adapted from [time]*/
		curtime = time(NULL);
		loc_time = localtime(&curtime);
		strftime (datebuf,TLEN,"%y%m%d.txt",loc_time);

		if ((datafilep=fopen(datebuf,"a")) !=NULL) { 
			/*If file exists, open for append*/
			/*If file didn't exist, create and open for append*/
			/*note: The datafile is opened and closed each data frame
			 * to allow other programs, e.g. LibreOffice Calc, Geany, 
			 * to refresh copies of the datafile while dmm is running.
			 *  */
		}	
		else { 	 
			/*file can't be opened */
			printf("Unable to open datalog file\n");
			exit(1);
		}
			
		tcflush(fd_dev,TCIFLUSH); /*flush input buffer */
		sleep(sec_per_sample);
		
		write(fd_dev,"D", 1);         
		/*activate data transmission from meter */
		/*Is this needed every frame? yes, works best if every frame*/

		/*create time stamp in near Virtual Weather Station format, 
		 * i.e. hr:mn:sc*/
		curtime = time(NULL);
		loc_time = localtime(&curtime);
		strftime (timebuf,TLEN,"%H,%M,%S",loc_time); /*csv format*/
		printf("%s, ",timebuf);
		fprintf(datafilep,"%s,",timebuf);
		i = 0;
		//strcpy(frame,""); /*empty the frame*/ /*is this line needed?*/
		do {
			read(fd_dev, &c, 1);
			frame[i++] = c;
			printf("%c",c);
			
		} while (c != '\r');
		//frame[i] = '\0'; 						/*is this line needed?*/
		printf("\n");
		
		substring(frame, frtype,1,2); /*extract frame type*/
		substring(frame, frvalue,3,6); /*extract frame value*/
		substring(frame, frunits,9,5); /*extract frame units*/
 
 		fprintf(datafilep,"%s,",frtype);
		fprintf(datafilep,"%s,",frvalue);
		fprintf(datafilep,"%s",frunits);
		fprintf(datafilep,"\n");
		
		fclose(datafilep); 
		/*close and reopen each frame to allow realtime log view
		 * and automatic daily file creation */
	} 
	
	fgets(kbstr,KBSTRLEN,stdin); 
	/*read stdin to keep from passing key presses*/
	/*to terminal command line on program exit*/

	close(fd_dev);
	exit(0);
}

int kbhit()
/* kbhit() -- key board hit test.
 * kbhit tests for key presses in a non-blocking way, i.e. kbhit() 
 * returns immediately without waiting for a keypress. 
 * If "enter" key is pressed; return=1; otherwise (no key or non-enter 
 * key), return=0. Adapted from [kbhit]:
*
* [kbhit]	http://cc.byexamples.com/2007/04/08/non-blocking-user-
* 			input-in-loop-without-ncurses

* 2016-0813, Scott Marin, code works. Non-enter key presses are 
* buffered at stdin on kbhit() exit. Use fgets() to read stdin to prevent
* passing key presses in stdin buffer to terminal command line.
*/
{
	struct timeval tv;
	fd_set fds;
	tv.tv_sec = 0;
	tv.tv_usec = 0;
	FD_ZERO(&fds);
	FD_SET(STDIN_FILENO,&fds); //STDIN_FILENO is 0
	int selval=select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
	int fd_issetval = FD_ISSET(STDIN_FILENO, &fds);
	return fd_issetval;
}

void substring(char s[FRAMELENGTH], char sub[FRAMELENGTH], int p, int l) {
	/*see [mid] 
	 * s = source string
	 * sub = substring
	 * p = start position in s for sub
	 * l = number of characters into sub
	 */
	 /* [mid]	Substring in c programming, 
  		http://www.programmingsimplified.com/c/source-code/c-substring*/

   int cc = 0;
  
   while (cc < l) {
      sub[cc] = s[p+cc-1];
      cc++;
   }
   //sub[cc] = '\0';                           /* is this step needed?*/
}
