I am new in Linux. I am facing problem in serial communications.
I have two computers, one is running a hyper terminal and on another I am writing a C program to communicate with hyper terminal. Both PC are connected through RS232.
I have written some code and I am able to send data to hyper terminal, but I am not sure how I can read data from hyper terminal with a program written in C on a Linux PC.
Any suggestions?
Thanks in advance.
Below is my code:
int main(int argc, char **argv)
{
struct termios options;
int fd;
fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
if(fd == -1)
{
printf("Could not open port /dev/ttyS0\n");
return 1;
}
tcgetattr(fd, &options); //get port options
cfsetispeed(&options, B9600); //set input baud rate
cfsetospeed(&options, B9600); //set output baud rate
options.c_cflag |= (CLOCAL | CREAD); //enable receiver and set local mode
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_cflag &= ~CRTSCTS; //disable hardware flow control
options.c_cflag &= ~(ICANON | ECHO | ISIG); //set raw input
options.c_cflag |= IXOFF; //disable software flow control
tcsetattr(fd, TCSANOW, &options); //set new port options
sleep(1);
int rc,count;
int size = 8;
unsigned char buf[10];
FILE *fp = NULL;
char ch;
int i=0;
printf("enter the data you want to send");
while((ch=getchar())!='\n')
{
write(fd,&ch,1);
}
close(fd);
printf("Finished.\n");
return 0;
}
Related
I have a C application which opens a /dev/ttyUSB* port
fd = open(portName.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_iflag &= (tcflag_t) ~(INLCR | IGNCR | ICRNL | IGNBRK);
options.c_oflag &= (tcflag_t) ~(OPOST);
options.c_cflag |= CS8;
options.c_cflag |= CRTSCTS;
options.c_lflag &= ~(ICANON | ECHO | ISIG);
tcsetattr(fd, TCSANOW, &options);
struct serial_struct kernel_serial_settings;
if (ioctl(fd, TIOCGSERIAL, &kernel_serial_settings) == 0) {
kernel_serial_settings.flags |= ASYNC_LOW_LATENCY;
kernel_serial_settings..
ioctl(fd, TIOCSSERIAL, &kernel_serial_settings);
}
The port is opened, and I receive data. If however the USB device is disconnected later on, and I call the read function:
nRead = _read(fd, buffer, length);
nRead will return 0. I would expect that it should return a negative number to indicate an error (that the device is no longer available).
How do I detect that the opened device was disconnected?
When read() returns zero, you can call stat() on the device filename. If the USB device is disconnected, the device node has vanished, and stat() will return -1 with errno==ENOENT.
I have been trying to read the responses from a serial temperature sensor interfaced to my raspberry pi using a USB to serial converter.
I can see that the writes to the sensor device seem to work. However when I try to read back from the serial chip the read fails with -1.
I did try to use the same baud rate 9600 8 bit no parity settings using realterm program and was able to read and write hex values as expected, kindly point me in the right direction.
void serial_write(char parameter,char value) {
int fd;
uint8_t bytes_wr;
char wr_buffer[3];
fd = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
ERROR("Error! in Opening ttyUSB0 \n");
else
DEBUG("ttyUSB0 Opened Successfully \n");
struct termios SerialPortSettings;
tcgetattr(fd, &SerialPortSettings);
cfsetispeed(&SerialPortSettings,B9600);
cfsetospeed(&SerialPortSettings,B9600);
SerialPortSettings.c_cflag &= ~PARENB;
SerialPortSettings.c_cflag &= ~CSTOPB;
SerialPortSettings.c_cflag &= ~CSIZE;
SerialPortSettings.c_cflag |= CS8;
SerialPortSettings.c_cflag &= ~CRTSCTS;
SerialPortSettings.c_cflag |= CREAD | CLOCAL;
SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY);
SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);
SerialPortSettings.c_oflag &= ~OPOST;
if ((tcsetattr(fd,TCSANOW,&SerialPortSettings)) != 0)
ERROR("ERROR ! in Setting attributes \n");
else
DEBUG("BaudRate=9600\tStopBits=1\tParity=none \n");
wr_buffer[0] = write;
wr_buffer[1] = parameter;
wr_buffer[2] = value;
bytes_wr = write(fd, wr_buffer,sizeof(wr_buffer));
DEBUG("Total Bytes written: %d \n", sizeof(wr_buffer));
close(fd);
}
The above function seems to write as expected to the serial port, however when I try to read, the reads fails with a -1
char serial_read(char parameter) {
int fd, read_length, i;
uint8_t bytes_wr;
char wr_buffer[2];
fd = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
ERROR("Error! in Opening ttyUSB0 \n");
else
DEBUG("ttyUSB0 Opened Successfully \n");
struct termios SerialPortSettings;
tcgetattr(fd, &SerialPortSettings);
cfsetispeed(&SerialPortSettings,B9600);
cfsetospeed(&SerialPortSettings,B9600);
SerialPortSettings.c_cflag &= ~PARENB;
SerialPortSettings.c_cflag &= ~CSTOPB;
SerialPortSettings.c_cflag &= ~CSIZE;
SerialPortSettings.c_cflag |= CS8;
SerialPortSettings.c_cflag &= ~CRTSCTS;
SerialPortSettings.c_cflag |= CREAD | CLOCAL;
SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY);
SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);
SerialPortSettings.c_oflag &= ~OPOST;
if ((tcsetattr(fd,TCSANOW,&SerialPortSettings)) != 0)
ERROR("ERROR ! in Setting attributes \n");
else
DEBUG("BaudRate=9600\tStopBits=1\tParity= none\n");
wr_buffer[0] = read;
wr_buffer[1] = parameter;
bytes_wr = write(fd, wr_buffer,sizeof(wr_buffer));
DEBUG("Total Bytes written: %d \n", sizeof(wr_buffer));
usleep(8000);
tcflush(fd,TCIFLUSH);
char rd_buffer[4];
read_length = read(fd, rd_buffer,sizeof(rd_buffer));
DEBUG("Total bytes read = %d \n",read_length);
for (i==0;i<read_length;i++){
DEBUG("rd_buffer[%d]=%x \n",i,rd_buffer[i]);
}
close(fd);
return rd_buffer[0];
}
With realterm windows application all writes and reads seem to work fine.
From the open(2) manpage:
O_NONBLOCK or O_NDELAY
When possible, the file is opened in nonblocking mode.
Neither the open() nor any subsequent operations on the file
descriptor which is returned will cause the calling process to
wait.
For a serial connection, the end result will be that if you ask to read some number of bytes from the serial port and there are no characters waiting, then read will return with -1 and 'errno' will probably be EAGAIN or EWOULDBLOCK.
So your usleep(8000) was probably an attempt to wait long enough for the device to respond but the device may not have data for you; especially if it is in the middle of an adc operation, it might take longer than 8ms.
There are a few things you can do:
You can (in pseudo code):
int retries=10;
while(retries--) {
read_length = read(fd, rd_buffer,sizeof(rd_buffer));
if(read_length > 0)
break;
usleep(1000);
}
Unfortunately, one side effect of this is that if the temperature sensor is sending you a lengthy string and your program read()s while the temperature sensor is still writing, you will get a partial string. So if you know the length of string that you're waiting to receive, you could use an ioctl() to find out how many characters are waiting:
ioctl(fd, FIONREAD, &bytes_avail);
So the pseudo code would look more like:
int retries=10;
int bytes_avail=0;
while(retries--) {
if (ioctl(fd, FIONREAD, &bytes_avail) < 0) {
fprintf(stderr, "ioctl failed\n");
return; // Do something here
}
if (bytes_avail >= sizeof(rd_buffer)) {
read_length = read(fd, rd_buffer,sizeof(rd_buffer));
if(read_length > 0)
break;
}
usleep(1000);
}
If the temperature sensor sends an ascii string that is terminated with a newline or carriage-return, then the code would look different.
I am trying to write c code on a Linux system, where I set the serial port parameters then open the serial port, then I have found out that even though the code compiles and runs, I cannot read and write from that serial port (so the serial port was not opened successfully)!
Code that works (not needed):
int fd;
char *portname;
portname = "/dev/ttyUSB0";
struct termios tty;
fd = open(portname, O_RDWR | O_NOCTYY | O_SYNC );
memset(&tty,0,sizeof tty);
tty.c_cflag &= ~PARENB;
tty.c_cflag &= ~CSTOP;
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8;
tty.c_cflag &= ~CRTSCTS;
tcsetattr(fd,TCSANOW,&tty);
Code that doesn't work (needed)
int fd;
char *portname;
portname = "/dev/ttyUSB0";
struct termios tty;
memset(&tty,0,sizeof tty);
tty.c_cflag &= ~PARENB;
tty.c_cflag &= ~CSTOP;
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8;
tty.c_cflag &= ~CRTSCTS;
tcsetattr(fd,TCSANOW,&tty);
fd = open(portname, O_RDWR | O_NOCTYY | O_SYNC );
Solid question: My application sequence requires me to set serial port parameters then open the serial port. Is there a way to do this? if yes, how?
I appreciate your help.
Update: Removed C++ code noticed by #Alter Mann.
Update: Removed zeroing-out the termios structure noticed by #sawdust.
In first case you get actual file descriptor fd and after use it. In second case you try setting up uninitialized file descriptor fd (probably 0 if it's declared in global scope) and after get actual value of it.
Below is workaround which works for me:
#include <fcntl.h>
#include <termios.h>
#include <strings.h>
#include <stdlib.h>
int main (int argc, char * argv [])
{
struct termios tty;
const char * portname = "/dev/ttyUSB0";
const int fd = open (portname, O_RDONLY);
if (-1 == fd) {
// Problem...
return EXIT_FAILURE;
}
if (tcgetattr (fd, &tty) < 0) {
// Problem...
return EXIT_FAILURE;
}
cfsetospeed (&tty, (speed_t) B9600);
cfsetispeed (&tty, (speed_t) B9600);
tty.c_cflag |= B9600;
tty.c_cflag |= (tty.c_cflag & ~CSIZE) | CS8;
tty.c_cflag |= (CLOCAL | CREAD);
tty.c_cflag &= ~(PARENB | PARODD);
tty.c_cflag |= IGNPAR;
tty.c_cflag &= ~(CRTSCTS);
tty.c_cflag &= ~(CSTOPB);
tty.c_iflag |= IGNBRK;
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
tty.c_lflag = 0;
tty.c_oflag = 0;
tcflush (fd, TCIFLUSH);
if (tcsetattr (fd, TCSANOW, &tty) ) {
// Problem...
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
I have try to access robot via serial port using gcc (i using mac).
I have made my program to send simple command:
HOME (enter) and there are some feedback from the robot: THIS IS THE
HOMING SEQUENCE COMMAND. THE ROBOT WILL SEEK ITS HOMING SWITCHES ON
EACH AXIS AND WILL EXECUTE THE HOMING FUNCTION AT THAT LOCATION. DO
YOU WANT TO PROCEED (Y/N) ?
and I send
Y(enter)
and the robot suppose move.
To access robot now is using modem/terminal Zterm.
This modem use baudrate 38400, 8N1
I use the same baudrate and 8n1
This is my code, I do not know what is wrong why my code cannot make the robot move
Thank you
Daniel
#include<stdio.h> /* Standard input/output definitions */
#include<stdlib.h>
#include<string.h> /* String function definitions */
#include<unistd.h> /* UNIX standard function definitions */
#include<fcntl.h> /* File control definitions */
#include<errno.h> /* Error number definitions */
#include<termios.h> /* POSIX terminal control definitions */
//#include<conio.h>
/*
* 'open_port()' - Open serial port 1.
*
* Returns the file descriptor on success or -1 on error.
*/
int buf_size;
char *buf;
char *buff;
int fd; /* File descriptor for the port */
int open_port(void)
{
fd = open("/dev/tty.USA28X1a2P2.2", O_RDWR | O_NOCTTY | O_NDELAY); // USA28X1a2P2.for keyspan
//fd = open("/dev/ttys000", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) {
/*
* Could not open the port.
*/
perror("cannot open");
}
else
fcntl(fd, F_SETFL, 0);
struct termios options;
/*
* Get the current options for the port...
*/
tcgetattr(fd, &options);
/*
* Set the baud rates to 38400...
*/
cfsetispeed(&options, B38400);
cfsetospeed(&options, B38400);
/*
* Enable the receiver and set local mode...
*/
options.c_cflag |= (CLOCAL | CREAD);
/*
* Set the new options for the port...
*/
tcsetattr(fd, TCSANOW, &options);
options.c_cflag &= ~CSIZE; /* Mask the character size bits */
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
return (fd);
}
int main(int argc, char**argv) {
buf =malloc(20);
buff=malloc(20);
// strcpy(buf,"HOME");
// strcpy(buff,"Y");
open_port();
printf("type the command using Capital Letter : \n");
scanf("%s",buf);
write(fd, buf,20); //
write(fd," \r ",5);
printf("Command = %s\n", buf);
read(fd, buff,50);
printf(" %s \n",buff);
free(buf);
free(buff);
printf("type Y/N : \n");
scanf("%s",buf);
write(fd, buf,2);
write(fd,"\r",2);
// free(buf);
// free (buff);
// printf("type Y/N : \n");
// write(fd, buf,20);
// printf("You choose %s \n",buff);
// free(buf);
close(fd);
}
You are reading too much at a time. You allocate your buffers as 20 bytes, buff=malloc(20); But here you read 50, read(fd, buff,50); That can lead to odd behavior at best. Make sure you allocate as much space as you'll use. You don't even need to allocate it, you can use an array.
char buf[1024];
char buff[1024];
Then you free your memory before using them again.
free(buf);
free(buff);
printf("type Y/N : \n");
scanf("%s",buf);
Don't free buf or buff until after you call close(fd).
Read up on good C style, too. You're doing a few things that aren't bugs, but will make your life harder later.
There are so many errors in the code (like buffer overruns, etc.), but the most obvious one I could spot is here:
tcsetattr(fd, TCSANOW, &options);
options.c_cflag &= ~CSIZE; /* Mask the character size bits */
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
You're setting the fields of the options structure only after you already used it for setting up the file descriptor's properties. You have to put the call to tcsetattr() to the end of this whole sequence:
options.c_cflag &= ~CSIZE; /* Mask the character size bits */
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
tcsetattr(fd, TCSANOW, &options);
If you don't want to mess with manually setting all this yourself, try my helper function.
I am testing the sending and receiving programs with the code as
The main() function is below:
#include "lib.h"
int fd;
int initport(int fd) {
struct termios options;
// Get the current options for the port...
tcgetattr(fd, &options);
// Set the baud rates to 19200...
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
// Enable the receiver and set local mode...
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
// Set the new options for the port...
tcsetattr(fd, TCSANOW, &options);
return 1;
}
int main(int argc, char **argv) {
fd = open("/dev/pts/2", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) {
perror("open_port: Unable to open /dev/pts/1 - ");
return 1;
} else {
fcntl(fd, F_SETFL, 0);
}
printf("baud=%d\n", getbaud(fd));
initport(fd);
printf("baud=%d\n", getbaud(fd));
char sCmd[254];
sCmd[0] = 0x41;
sCmd[1] = 0x42;
sCmd[2] = 0x43;
sCmd[3] = 0x00;
if (!writeport(fd, sCmd)) {
printf("write failed\n");
close(fd);
return 1;
}
printf("written:%s\n", sCmd);
usleep(500000);
char sResult[254];
fcntl(fd, F_SETFL, FNDELAY);
if (!readport(fd,sResult)) {
printf("read failed\n");
close(fd);
return 1;
}
printf("readport=%s\n", sResult);
close(fd);
return 0;
}
The lib.h contains read and write code as at:
Parse and read data frame in C?
and got the issue:
In order to test with serial port, I used the socat (https://help.ubuntu.com/community/VirtualSerialPort ) to create a pair serial ports on Linux and test my program with these port.
The first time the program sends the data and the program receives data is ok. However, if I read again or even re-write the new data into the serial port, the return data is always null until I stop the virtual serial port and start it again, then the write and read data is ok, but still, only one time.
(In the real case, the sending part will be done by another device, I am just taking care of the reading data from the serial port. I wrote both parts just to test my reading code.)
Does anyone have any ideas?
Either your comment or your code is wrong:
// Set the baud rates to 19200...
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
This says it will be setting the baud rate to 19200, but it really sets it to 9600. Maybe you want this:
// Set the baud rates to 19200...
cfsetispeed(&options, B19200);
cfsetospeed(&options, B19200);