Solved

Serial Linux C App Issues

Posted on 2006-07-18
9
304 Views
Last Modified: 2010-05-18
I have a SmartRelay device that I need to create a "driver" for. I'm just trying to get this working at a very simple level first and then continue from there.

I can drive the SmartRelay device from HyperTerminal in windows with 9600 8n1 settings and everything works successfully.

I have been able to capture the output from my comtest.c driver in HyperTerminal and it displays as expected.

Here is the c code:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>

/* baudrate settings are defined in <asm/termbits.h>, which is
included by <termios.h> */
#define BAUDRATE B9600
/* change this definition for the correct port */
#define DEVICE "/dev/ttyS0"

#define FALSE 0
#define TRUE 1

int open_port(void)
{
      int fd;
      
      fd = open(DEVICE, O_RDWR | O_NOCTTY | O_NDELAY);
      if (fd == -1)
      {
            perror("open_port: Unable to open port - ");
      }
      else
      {
            fcntl(fd, F_SETFL, 0);
      }
      
      return (fd);
}

int main()
{
      int fd,c, res;
      struct termios oldtio,newtio;
      char buf[255];
      char mystring[255];
      /*
      Open modem device for reading and writing and not as controlling tty
      because we don't want to get killed if linenoise sends CTRL-C.
      */
printf("open port\n");
      fd = open_port();
      if (fd < 0) {
            perror(DEVICE);
            exit(-1);
      }
printf("port opened\n");      
      tcgetattr(fd,&oldtio); /* save current serial port settings */
printf("got attribs\n");      
      bzero(&newtio, sizeof(newtio)); /* clear struct for new port settings */
printf("zeroed attribs\n");      
      /*
            BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
            CRTSCTS : output hardware flow control (only used if the cable has
                              all necessary lines. See sect. 7 of Serial-HOWTO)
            CS8     : 8n1 (8bit,no parity,1 stopbit)
            CLOCAL  : local connection, no modem contol
            CREAD   : enable receiving characters
      */
      newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
      
      /*
            IGNPAR  : ignore bytes with parity errors
            ICRNL   : map CR to NL (otherwise a CR input on the other computer
                              will not terminate input)
                              otherwise make device raw (no other input processing)
      */
      newtio.c_iflag = IGNPAR | ICRNL;
      
      /*
      Raw output.
      */
      newtio.c_oflag = 0;
      
      /*
            now clean the line and activate the settings for the port
      */
printf("tcflush\n");      
      tcflush(fd, TCIFLUSH);
printf("tcsetattr\n");      
      tcsetattr(fd,TCSANOW,&newtio);
      
      /*
            terminal settings done, now handle input
            In this example, inputting a 'z' at the beginning of a line will
            exit the program.
      */

      printf("writing to port\n");
      if (-1 == write(fd, "1R1\r", 4))
      {
            perror("Error writing 1R1\n");
            exit(1);
      }
      sleep(1);
      if (-1 == write(fd, "1R0\r", 4))
      {
            perror("Error writing 1R0\n");
            exit(1);
      }
      sleep(1);
      if (-1 == write(fd, "2R1\r", 4))
      {
            perror("Error writing 2R1\n");
            exit(1);
      }
      sleep(1);
      if (-1 == write(fd, "2R0\r", 4))
      {
            perror("Error writing 2R0\n");
            exit(1);
      }
      sleep(1);
      if (-1 == write(fd, "3R1\r", 4))
      {
            perror("Error writing 3R1\n");
            exit(1);
      }
      sleep(1);
      if (-1 == write(fd, "3R0\r", 4))
      {
            perror("Error writing 3R0\n");
            exit(1);
      }
      sleep(1);
      
      /* restore the old port settings */
printf("restore attribs\n");      
      tcsetattr(fd,TCSANOW,&oldtio);
}

Here is the stty from the comport after breaking within the application:
[root@ScanVue bin]# stty < /dev/ttyS0
speed 9600 baud; line = 0;
intr = <undef>; quit = <undef>; erase = <undef>; kill = <undef>; eof = <undef>;
start = <undef>; stop = <undef>; susp = <undef>; rprnt = <undef>;
werase = <undef>; lnext = <undef>; flush = <undef>; min = 0; time = 0;
-brkint -imaxbel
-opost -onlcr
-isig -icanon -iexten -echo -echoe -echok -echoctl -echoke
0
Comment
Question by:r_i_x
  • 5
  • 3
9 Comments
 
LVL 8

Expert Comment

by:manish_regmi
ID: 17131462
hi r_i_x,
    You are not using any of the old attributes but using your new. In the new structure, you have only used few attributes. the rest is set to zero.

but you should copy the old attributes to new attributes and change only the attributes you need.


struct termios oldtio,newtio;

 tcgetattr(fd,&oldtio); /* save current serial port settings */
memcpy(&newio, &oldio, sizeof(struct termios));
 newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
...... whatever you want to change
tcsetattr(fd,TCSANOW,&newtio);


regards
Manish Regmi
0
 
LVL 8

Expert Comment

by:manish_regmi
ID: 17131507
the problem with your code was that after you set the terminal attributes, a lot of attributes was set to 0,

like
c_cc array.
so all the characters will be set to 0, like
VEOF, VEOL, VERASE, VKILL

so, the terminal driver will take EOF, EOL etc as 0.

regards
Manish Regmi
0
 
LVL 1

Author Comment

by:r_i_x
ID: 17133669
Thanks for the help. I've added that code but I don't think it has made a difference (although likely it is cleaner). Here is the stty output now:

[root@ScanVue bin]# stty < /dev/ttyS0
speed 9600 baud; line = 0;
min = 1; time = 0;
-brkint -icrnl -imaxbel
-opost -onlcr
-icanon -echo -echoe -echok -echoctl -echoke

The data I'm sending is simply "1R1\r". The first 3 chars are a command and then a carriage return.

Supposed to be setup with 9600bps, 8 data bits, parity = none, 1 stop bit, and no flow control. Does the stty show this correctly?
0
 
LVL 8

Expert Comment

by:manish_regmi
ID: 17135318
it has corrected your our output

previously, it was
ntr = <undef>; quit = <undef>; erase = <undef>; kill = <undef>; eof = <undef>;
start = <undef>; stop = <undef>; susp = <undef>; rprnt = <undef>;

it meant that all those characters were undefined. means you could not use any of those control characters like kill, erase start etc.

-icanon -echo -echoe -echok -echoctl -echoke
means canonical mode and echo is on

see this howto
http://www.erlenstar.demon.co.uk/unix/faq_4.html

regards
Manish Regmi

0
3 Use Cases for Connected Systems

Our Dev teams are like yours. They’re continually cranking out code for new features/bugs fixes, testing, deploying, testing some more, responding to production monitoring events and more. It’s complex. So, we thought you’d like to see what’s working for us.

 
LVL 8

Expert Comment

by:manish_regmi
ID: 17135324
also see what posix has to say on terminal i/o

http://www.opengroup.org/pubs/online/7908799/xbd/termios.html

regards
Manish Regmi
0
 
LVL 1

Author Comment

by:r_i_x
ID: 17135445
Can you tell by what I've posted if I have everything setup properly for 8n1 and no harware flow control?
0
 
LVL 8

Accepted Solution

by:
manish_regmi earned 500 total points
ID: 17135672
ooh,
 newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;

should be

newtio.c_cflag &= ~CSIZE; // turn of other bits if already set
newtio.c_cflag |= CS8;    // set cs8 bit only

 newtio.c_cflag |= BAUDRATE | CLOCAL | CREAD;

regards
Manish Regmi



0
 

Expert Comment

by:KubaOber
ID: 17278801
Long time ago I've posted a relatively generic way of setting an arbitrary baudrate for most Linux serial devices. Here's the link:
http://hrabia.ibib.waw.pl/~winnie/baudrate.html

This is mildly related to your question; it will come handy if you ever have to deal with non-standard baud rates.
0
 
LVL 1

Author Comment

by:r_i_x
ID: 17278937
Thanks for the assistance. Turns out the pinouts on the device required a NULL modem cable.

The settings worked well.
0

Featured Post

Does Powershell have you tied up in knots?

Managing Active Directory does not always have to be complicated.  If you are spending more time trying instead of doing, then it's time to look at something else. For nearly 20 years, AD admins around the world have used one tool for day-to-day AD management: Hyena. Discover why

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Have you thought about creating an iPhone application (app), but didn't even know where to get started? Here's how: ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Important pre-programming comments: I’ve never tri…
An Outlet in Cocoa is a persistent reference to a GUI control; it connects a property (a variable) to a control.  For example, it is common to create an Outlet for the text field GUI control and change the text that appears in this field via that Ou…
The goal of this video is to provide viewers with basic examples to understand and use structures in the C programming language.
Video by: Grant
The goal of this video is to provide viewers with basic examples to understand and use for-loops in the C programming language.

863 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

23 Experts available now in Live!

Get 1:1 Help Now