accessing robot via Serial port - c

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.

Related

read() is blocked forever on raspberry pi 3 UART

The C read() function frequently get blocked, especially if there is nothing connected on the Gpio pins (tx / rx), but I just hoped it would stop for itself when there is no conection, the same when there is wire conection but no data to read, but it just get blocked until I force it to finish.
Open
fd = open("/dev/serial0", O_RDWR | O_NDELAY | O_NOCTTY | O_NONBLOCK);
Read
n = read( fd, value, 1 );
if (n < 0) {
printf ( "Error = %s\n", strerror( errno ) );
}
else if (n == 0) {
printf ( "Read Nothing...\n");
}
Set attribs
int setAttr(int fd)
{
//Read the configureation of the port
struct termios options;
tcgetattr( fd, &options );
//Set Baud Rate
cfsetispeed( &options, B9600 );
cfsetospeed( &options, B9600 );
//Setting other Port Stuff
options.c_cflag &= ~PARENB; /*Make 8n1 */
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE; /* Mask the character size bits */
options.c_cflag |= CS8; /* Select 8 data bits */
options.c_cflag &= ~CRTSCTS; /* No flow control */
options.c_cc[VMIN] = 0; /*READ doesn't block */
options.c_cc[VTIME] = 1; /* 0.1 seconds read timout */
options.c_cflag |= CREAD | CLOCAL; /* Turn on READ & ignore crtl lines */
//Make raw
cfmakeraw(&options);
//Flush port, then applies attributes
tcflush(fd, TCIOFLUSH);
return tcsetattr( fd, TCSANOW, &options );
}
For a UART port with nothing but tx/rx pins, there is no distinct "nothing connected" status. For the functionality you want, the port would need DCE/DTR pins and the CLOCAL flag (ignore modem control lines) would have to be removed from the termios settings.
I just add the code below, after open() and it's not blocked anymore when tx/rx pins are not connected. Probably the port was going blocked after open().
if (fd == -1)
{
/*
* Could not open the port.
*/
perror("open_port: Unable to open /dev/serial0 - ");
}
else fcntl(fd, F_SETFL, O_NONBLOCK);

reading from serial port always returns what was written

I'm using a Raspberry Pi Zero in device mode and a Raspberry Pi B in host mode. I'm connecting the two with a USB cable. My goal right now is just to send simple, arbitrary data back and forth between the two Pi's.
The problem is whichever Pi writes to the serial port first ends up reading what it wrote. The program I've written has the device send d\n and the host send h\n. So, if the device writes first, the the host reads correctly the d\n, then writes h\n to the serial port. But the device ends up reading d\n! The problem persists if I switch it so that the host writes first.
I've tried adding various tcflush calls to the program after writing but before reading, but it doesn't work. I've also tried sleeping for various amounts of time. I've read waiting 100 microseconds for each character written and I've slept for several seconds.
My setup requires me to not have a constant connection between both Pi's at the same time because of the Pi Zero's single data-capable usb port. So, to test, I'm actually plugging in a keyboard and running the program, then plugging in the proper cable to transfer data. I can transfer data, but not after writing because the program simply reads back what it wrote.
I'm starting to think I've fallen into a noob trap that I can't fathom. Here is the code I'm using:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
/*
* gcc -o device_rw -DDEVICE serial_rw.c
* gcc -o host_rw serial_rw.c
*/
#define SERIAL_DEVICE "/dev/ttyGS0"
#define SERIAL_HOST "/dev/ttyACM0"
#ifdef DEVICE
#define _TTY SERIAL_DEVICE
#else
#define _TTY SERIAL_HOST
#endif
int
set_interface_attribs(int fd, int speed)
{
struct termios tty;
if (tcgetattr(fd, &tty) < 0) {
printf("Error from tcgetattr: %s\n", strerror(errno));
return -1;
}
cfsetospeed(&tty, (speed_t)speed);
cfsetispeed(&tty, (speed_t)speed);
tty.c_cflag &= ~PARENB; /* no parity bit */
tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; /* 8-bit characters */
tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
tty.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */
tty.c_iflag |= IGNPAR | IGNCR;
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
tty.c_iflag |= ICANON;
tty.c_iflag &= ~OPOST;
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
printf("Error from tcsetattr: %s\n", strerror(errno));
return -1;
}
return 0;
}
void
write_serial (int fd, const char *buf, int len)
{
printf("WRITE: %s\n", buf);
write(fd, buf, len);
}
void
read_serial (int fd, char *buf, int len)
{
ssize_t nread = read(fd, buf, len);
if (nread > 0 && nread <= len) {
buf[nread] = 0;
printf(" READ: %s\n", buf);
}
}
int
main (int argc, char **argv)
{
char buf[80];
int fd = open(_TTY, O_RDWR | O_NOCTTY);
if (fd < 0) {
fprintf(stderr, "Can't open %s: %s\n", _TTY, strerror(errno));
goto exit;
}
if (set_interface_attribs(fd, B115200) < 0) {
goto exit;
}
#ifdef DEVICE
printf("device: %s\n", _TTY);
write_serial(fd, "d\n", 2);
usleep((2 + 25) * 100);
read_serial(fd, buf, 2);
#else
printf("host: %s\n", _TTY);
read_serial(fd, buf, 2);
//usleep((2 + 25) * 100);
write_serial(fd, "h\n", 2);
#endif
close(fd);
exit:
return 0;
}
Beside not disabling the ECHO attributes (as #MarkPlotnick commented), you have two misapplied assignments:
tty.c_iflag |= ICANON;
ICANON belongs to the lflag member, and
tty.c_iflag &= ~OPOST;
OPOST belongs to the oflag member.
Considering these errors, did you properly apply #MarkPlotnick's suggestion?
See Working with linux serial port in C, Not able to get full data for a working canonical setup.
cfsetospeed(&tty, (speed_t)speed);
cfsetispeed(&tty, (speed_t)speed);
tty.c_cflag |= CLOCAL | CREAD;
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; /* 8-bit characters */
tty.c_cflag &= ~PARENB; /* no parity bit */
tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
tty.c_lflag |= ICANON | ISIG; /* canonical input */
tty.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN);
tty.c_iflag &= ~INPCK;
tty.c_iflag |= IGNCR;
tty.c_iflag &= ~(INLCR | ICRNL | IUCLC | IMAXBEL);
tty.c_iflag &= ~(IXON | IXOFF | IXANY); /* no SW flowcontrol */
tty.c_oflag &= ~OPOST;
Note that it is also possible to have echo enabled on the far end.
To determine if the echo is generated locally or from the far end, simply disconnect the remote device (assuming you're using UARTs and/or USB-to-serial adapters), and transmit.
If you still get echo, then it's generated locally, which is controlled by the termios ECHO attributes.
If you no longer get an echo, then it's the remote unit that is repeating its input back to the sender.
BTW Your program as posted does not compile cleanly.
It's missing #include <string.h>
The termios initialization routine that you copied (but then incorrectly modified) has proper checking of return values, but your read and write routines do not check for errors.
I learned a lot from everyone's answers and am grateful for them because I will need them as I move on with this project. For this particular case, however, the issue ended up being permissions. Yep, file permissions on ttyGSO.
I completely expected a permission denied error from open and never got one, so I never considered the possibility. Especially since I opened the files in RDWR mode, I could write to the serial file, and it appeared I was reading data (although the same data) it never dawned on me that perhaps I didn't have read permissions.

Read from serial port linux

I'm trying to read from serial port, but always get 0 (zero) characters back. Already read the "Serial Programming Guide for POSIX Operating Systems", but can't find out why the program not waiting (blocking).
The code:
#include <stdio.h> /* Standard input/output definitions */
#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 */
void main()
{
printf("Hello world\n");
int fd; /* File descriptor for the port */
int n;
int bytes;
char c;
char buffer[10];
char *bufptr;
struct termios options;
fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) {
perror("open_port: Unable to open /dev/ttyUSB0 - ");
}
else {
fcntl(fd, F_SETFL, FNDELAY);
}
tcgetattr( fd, &options );
/* SEt Baud Rate */
cfsetispeed( &options, B9600 );
cfsetospeed( &options, B9600 );
//I don't know what this is exactly
options.c_cflag |= ( CLOCAL | CREAD );
// Set the Charactor size
options.c_cflag &= ~CSIZE; /* Mask the character size bits */
options.c_cflag |= CS8; /* Select 8 data bits */
// Set parity - No Parity (8N1)
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
// Disable Hardware flowcontrol
// options.c_cflag &= ~CNEW_RTSCTS; -- not supported
// Enable Raw Input
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
// Disable Software Flow control
options.c_iflag &= ~(IXON | IXOFF | IXANY);
// Chose raw (not processed) output
options.c_oflag &= ~OPOST;
if ( tcsetattr( fd, TCSANOW, &options ) == -1 )
printf ("Error with tcsetattr = %s\n", strerror ( errno ) );
else
printf ( "%s\n", "tcsetattr succeed" );
fcntl(fd, F_SETFL, 0);
// Write to the port
n = write(fd, "1", 1);
if (n < 0) {
fputs("write() of 1 bytes failed!\n", stderr);
}
// Read from the port
//fcntl(fd, F_SETFL, FNDELAY);
bytes = read(fd, &buffer, sizeof(buffer));
printf("number of bytes read is %d\n", bytes);
printf("%s\n", buffer);
//perror ("read error:");
close(fd);
}
This information was originally from the Serial Programming Guide1.
The reason you are getting a 0 return value is because of this line:
fcntl(fd, F_SETFL, FNDELAY);
If you want a normal blocking read, unset that flag.
1. http://www.easysw.com/~mike/serial/serial.html#2_5_4 (now defunct)
You are using O_NDELAY
O_NONBLOCK or O_NDELAY
When possible, the file is opened in non-blocking mode. Neither the open() nor any subsequent operations on the file
descriptor which is returned will cause the calling process to
wait. For the handling of FIFOs (named pipes), see also fifo(7). For
a discussion of the effect of O_NONBLOCK in conjunction with mandatory
file locks and with file leases, see fcntl(2).
EDIT: You're doing the same thing in your fcntl() call, as well.

serial communication through rs232 in linux c

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;
}

Reading and writing binary data over serial port

So I searched around, and couldn't exactly find what I needed. I need help reading and writing binary data over a serial port, and would appreciate any advice you may have. Please note, I asked a question similar to this earlier when I was at a different stage of this project.
Below are programs. The first program opens a file "test.jpg", reads it in binary mode and stores the result in a buffer. It then closes the file, and is supposed to send that file over a serial port.
The second program creates a file called "testout.jpg", and is supposed to read in the data sent from the previous program.
I have a hunch that the problem in my code lies in the second program. Perhaps I need to use fread for that too? I tried, but I cannot figure out how to implement it for a serial port as I am relatively new to programming.
Many thanks for your time.
Serial write:
#include <stdio.h> /* Standard input/output definitions */
#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 <stdlib.h>
int main()
{
//writing
int writeport = open_port("/dev/ttyUSB0");
//open file
FILE *file;
char *buffer;
int fileLen;
file = fopen("test.jpg", "rb");
//get file size
fseek(file, 0, SEEK_END);
fileLen = ftell(file);
fseek(file, 0, SEEK_SET);
buffer = (char *)malloc(fileLen + 1);
//read file contents
fread(buffer, fileLen, 1, file);
fclose(file);
int n = write(writeport, buffer, fileLen + 1);
if (n < 0)
fputs("write() of bytes failed!\n", stderr);
//closing ports
close(writeport);
}
int open_port(char str[])
{
int fd = open(str, O_RDWR | O_NOCTTY | O_NONBLOCK); // ?? NDELAY or NONBLOCK?
if (fd == -1)
{
perror("open_port: Unable to open /dev/ttyS0 - ");
}
else
fcntl(fd, F_SETFL, 0);
struct termios options;
tcgetattr(fd, &options); //this gets the current options set for the port
// setting the options
cfsetispeed(&options, B9600); //input baudrate
cfsetospeed(&options, B9600); // output baudrate
options.c_cflag |= (CLOCAL | CREAD); // ?? enable receicer and set local mode
//options.c_cflag &= ~CSIZE; /* mask the character size bits */
options.c_cflag |= CS8; /* select 8 data bits */
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // choosing raw input
options.c_iflag &= ~INPCK; // disable parity check
options.c_iflag &= ~(IXON | IXOFF | IXANY); // disable software flow control
options.c_oflag |= OPOST; // ?? choosing processed output
options.c_cc[VMIN] = 0; // Wait until x bytes read (blocks!)
options.c_cc[VTIME] = 0; // Wait x * 0.1s for input (unblocks!)
// settings for no parity bit
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
tcsetattr(fd, TCSANOW, &options); //set the new options ... TCSANOW specifies all option changes to occur immediately
return (fd);
}
Serial read:
#include <stdio.h> /* Standard input/output definitions */
#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 */
int main()
{
//reading
int readport = open_port("/dev/ttyUSB1");
//open resultant file
FILE *file;
//system("rm testout.jpg");
file = fopen("testout.jpg", "wb");
//trying to read one character at a time
char buff;
int n = 1;
while (n > 0)
{
n = read(readport, &buff, 1);
//printf("%c", buff, buff);
**//I tried these three methods, with little success**
//fprintf(file, "%c", buff);
//fwrite(&buff, 1, 1, file);
//write(file, &buff, 1);
}
//closing ports
close(readport);
fclose(file);
}
int open_port(char str[])
{
int fd = open(str, O_RDWR | O_NOCTTY | O_NONBLOCK); // ?? NDELAY or NONBLOCK?
if (fd == -1)
{
perror("open_port: Unable to open /dev/ttyS0 - ");
}
else
fcntl(fd, F_SETFL, 0);
struct termios options;
tcgetattr(fd, &options); //this gets the current options set for the port
// setting the options
cfsetispeed(&options, B9600); //input baudrate
cfsetospeed(&options, B9600); // output baudrate
options.c_cflag |= (CLOCAL | CREAD); // ?? enable receicer and set local mode
//options.c_cflag &= ~CSIZE; /* mask the character size bits */
options.c_cflag |= CS8; /* select 8 data bits */
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // choosing raw input
options.c_iflag &= ~INPCK; // disable parity check
options.c_iflag &= ~(IXON | IXOFF | IXANY); // disable software flow control
options.c_oflag |= OPOST; // ?? choosing processed output
options.c_cc[VMIN] = 0; // Wait until x bytes read (blocks!)
options.c_cc[VTIME] = 0; // Wait x * 0.1s for input (unblocks!)
// settings for no parity bit
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
tcsetattr(fd, TCSANOW, &options); //set the new options ... TCSANOW specifies all option changes to occur immediately
return (fd);
}
file = fopen( "zname.jpg", "wb" );
while (1) {
n = read(readport, &buff, 1);
if (n == -1) switch(errno) {
case EAGAIN: /* sleep() */
continue;
...
default: goto quit;
}
if (n ==0) break;
fputc(buff, file);
}
quit:
fclose (file);
...
Even better than sleep() and loop, would be to use select/poll. (You'd still have to check for EAGAIN)

Resources