I am trying to write a simple application to read out a current value from a Keithley 6485 picoammeter, connected via serial communication (RS232<->USB) on linux.
Currently, such a value can be retrieved by doing all the needed initialization of the device and sending "READ?" to it: echo "READ?" > /dev/ttyUSB0
. Then if cat /dev/ttyUSB0
has been listening, I get the following output: -2.250416E-14A,+8.320175E+03,+0.000000E+00
,of which the first number is the desired value.
To be able to output the value I use the following code using termios libraries:
/*====================================================================================================*/
/* Serial Port Programming in C (Serial Port Read) */
/* Non Cannonical mode */
/*----------------------------------------------------------------------------------------------------*/
/* Program reads a string from the serial port at 9600 bps 8N1 format */
/* Baudrate - 9600 */
/* Stop bits -1 */
/* No Parity */
/*----------------------------------------------------------------------------------------------------*/
/* Compiler/IDE : gcc 4.6.3 */
/* Library : */
/* Commands : gcc -o serialport_read serialport_read.c */
/* OS : Linux(x86) (Linux Mint 13 Maya)(Linux Kernel 3.x.x) */
/* Programmer : Rahul.S */
/* Date : 21-December-2014 */
/*====================================================================================================*/
/*====================================================================================================*/
/* www.xanthium.in */
/* Copyright (C) 2014 Rahul.S */
/*====================================================================================================*/
/*====================================================================================================*/
/* Running the executable */
/* ---------------------------------------------------------------------------------------------------*/
/* 1) Compile the serialport_read.c file using gcc on the terminal (without quotes) */
/* */
/* " gcc -o serialport_read serialport_read.c " */
/* */
/* 2) Linux will not allow you to access the serial port from user space,you have to be root.So use */
/* "sudo" command to execute the compiled binary as super user. */
/* */
/* "sudo ./serialport_read" */
/* */
/*====================================================================================================*/
/*====================================================================================================*/
/* Sellecting the Serial port Number on Linux */
/* ---------------------------------------------------------------------------------------------------*/
/* /dev/ttyUSBx - when using USB to Serial Converter, where x can be 0,1,2...etc */
/* /dev/ttySx - for PC hardware based Serial ports, where x can be 0,1,2...etc */
/*====================================================================================================*/
/*-------------------------------------------------------------*/
/* termios structure - /usr/include/asm-generic/termbits.h */
/* use "man termios" to get more info about termios structure */
/*-------------------------------------------------------------*/
#include <stdio.h>
#include <fcntl.h> /* File Control Definitions */
#include <termios.h> /* POSIX Terminal Control Definitions */
#include <unistd.h> /* UNIX Standard Definitions */
#include <errno.h> /* ERROR Number Definitions */
void main(void)
{
int fd;/*File Descriptor*/
printf("\n +----------------------------------+");
printf("\n | Serial Port Read |");
printf("\n +----------------------------------+");
/*------------------------------- Opening the Serial Port -------------------------------*/
/* Change /dev/ttyUSB0 to the one corresponding to your system */
fd = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY); /* ttyUSB0 is the FT232 based USB2SERIAL Converter */
// fd = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY | O_NDELAY); /* ttyUSB0 is the FT232 based USB2SERIAL Converter */
/* O_RDWR - Read/Write access to serial port */
/* O_NOCTTY - No terminal will control the process */
/* Open in blocking mode,read will wait */
if(fd == -1) /* Error Checking */
printf("\n Error! in Opening ttyUSB0 ");
else
printf("\n ttyUSB0 Opened Successfully ");
/*---------- Setting the Attributes of the serial port using termios structure --------- */
struct termios SerialPortSettings; /* Create the structure */
tcgetattr(fd, &SerialPortSettings); /* Get the current attributes of the Serial port */
/* Setting the Baud rate */
cfsetispeed(&SerialPortSettings,B19200); /* Set Read Speed as 19200 */
cfsetospeed(&SerialPortSettings,B19200); /* Set Write Speed as 19200 */
/* 8N1 Mode */
SerialPortSettings.c_cflag &= ~PARENB; /* Disables the Parity Enable bit(PARENB),So No Parity */
SerialPortSettings.c_cflag &= ~CSTOPB; /* CSTOPB = 2 Stop bits,here it is cleared so 1 Stop bit */
SerialPortSettings.c_cflag &= ~CSIZE; /* Clears the mask for setting the data size */
SerialPortSettings.c_cflag |= CS8; /* Set the data bits = 8 */
SerialPortSettings.c_cflag &= ~CRTSCTS; /* No Hardware flow Control */
SerialPortSettings.c_cflag |= CREAD | CLOCAL; /* Enable receiver,Ignore Modem Control lines */
SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY); /* Disable XON/XOFF flow control both i/p and o/p */
SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* Non Cannonical mode */
SerialPortSettings.c_oflag &= ~OPOST;/*No Output Processing*/
/* Setting Time outs */
SerialPortSettings.c_cc[VMIN] = 13; /* Read at least 10 characters */
SerialPortSettings.c_cc[VTIME] = 0; /* Wait indefinetly */
if((tcsetattr(fd,TCSANOW,&SerialPortSettings)) != 0) /* Set the attributes to the termios structure*/
printf("\n ERROR ! in Setting attributes");
else
printf("\n BaudRate = 19200 \n StopBits = 1 \n Parity = none");
/*------------------------------- Read data from serial port -----------------------------*/
char read_buffer[32]; /* Buffer to store the data received */
int bytes_read = 0; /* Number of bytes read by the read() system call */
int i = 0;
tcflush(fd, TCIFLUSH); /* Discards old data in the rx buffer */
bytes_read = read(fd,&read_buffer,32); /* Read the data */
printf("\n\n Bytes Rxed -%d", bytes_read); /* Print the number of bytes read */
printf("\n\n ");
for(i=0;i<13;i++) /*printing only the needed bytes*/
printf("%c",read_buffer[i]);
printf("\n +----------------------------------+\n\n\n");
close(fd); /* Close the serial port */
}
Which outputs:
+----------------------------------+
| Serial Port Read |
+----------------------------------+
ttyUSB0 Opened Successfully
BaudRate = 19200
StopBits = 1
Parity = none
Bytes Rxed -13
-2.864104E-14
+----------------------------------+
However, for it to be able to read the value, I have to echo the "READ?" command (or write to the device by using a write() function) every time when I want to know the value.
How can I put both writing and reading to the same application in the most elegant way (e.g. without making of named pipes), as currently the read() function waits for anything to come from the device and during that time I can not send a "READ?" command from the same C code?
EDIT: Apparently my writing procedure does not seem to work properly:
Port settings:
struct termios SerialPortSettings; /* Create the structure */
tcgetattr(fd, &SerialPortSettings); /* Get the current attributes of the Serial port */
cfsetispeed(&SerialPortSettings,(speed_t)B19200); /* Set Read Speed as 19200 */
cfsetospeed(&SerialPortSettings,(speed_t)B19200); /* Set Write Speed as 19200 */
SerialPortSettings.c_cflag &= ~PARENB; /* Disables the Parity Enable bit(PARENB),So No Parity */
SerialPortSettings.c_cflag &= ~CSTOPB; /* CSTOPB = 2 Stop bits,here it is cleared so 1 Stop bit */
SerialPortSettings.c_cflag &= ~CSIZE; /* Clears the mask for setting the data size */
SerialPortSettings.c_cflag |= CS8; /* Set the data bits = 8 */
SerialPortSettings.c_cflag = ~CRTSCTS; /* No Hardware flow Control */
SerialPortSettings.c_cflag |= CREAD | CLOCAL; /* Enable receiver,Ignore Modem Control lines */
cfmakeraw(&SerialPortSettings);
tcflush(fd,TCIFLUSH);
SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY); // Disable XON/XOFF flow control both i/p and o/p
SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Non Cannonical mode
SerialPortSettings.c_oflag &= ~OPOST;//No Output Processing
Writing:
char write_buffer[] = "READ?"; /* Buffer containing characters to write into port */
int bytes_written = 0; /* Value for storing the number of bytes written to the port */
bytes_written = write(fd,&write_buffer,sizeof(write_buffer) -1);
printf("%s written to ttyUSB0 \n",write_buffer);
printf("%d Bytes written to ttyUSB0 \n", bytes_written);
printf("+----------------------------------+\n\n");
close(fd);/* Close the Serial port */
Whenever this runs, I get:
+----------------------------------+
| Serial Port Write |
+----------------------------------+
ttyUSB0 Opened Successfully
Attributes set
READ? written to ttyUSB0
5 Bytes written to ttyUSB0
+----------------------------------+
But cat /dev/ttyUSB0
does not seem to see anything coming from the device. I have checked the similar question at:
Linux C Serial Port Reading/Writing
and can not find big differences in code - would that be a sign of wrong port settings or am I missing something?