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?
The problem has been fixed!
Apparently, the device was waiting for an end-line terminator, which was not available in char write_buffer[] = "READ?";
Therefore when it received such a command, the bus would "hang" and in order for it to work again one would have to open the port again.
This also explains why the echo "READ?" > /dev/ttyUSB0
command worked - since it automatically outputs an end-line terminator.
I attach the working code below - thank you everyone for your commments and kind help!
int fd; //device file id
//------------------------------- Opening the Serial Port -------------------------------
fd = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY); // ttyUSB0 is the FT232 based USB2SERIAL Converter
if(fd == -1) // Error Checking
printf("Error while opening the device\n");
//---------- 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
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("Error while setting attributes \n");
//------------------------------- 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 bytes_written = 0; // Number of bytes written
int i = 0;
tcflush(fd, TCIFLUSH); // Discards old data in the rx buffer
//Device intialization
char write_buffer[]="READ? \n ";
bytes_written=write(fd,&write_buffer,sizeof(write_buffer));
bytes_read = read(fd,&read_buffer,32); // Read the data
for(i=0;i<13;i++) //printing only the needed characters
printf("%c",read_buffer[i]);
close(fd); // Close the serial port