(E-Mail Removed) (Joshua Colvin) wrote:
>Hello all,
>I'm trying to setup a socket connection to a serial port under Linux.
>When talking to the device on the other end of the serial port I get
>an SI (shift in) character (hex value 0x0f, octal value 017) embedded
>in the response. So rather than get "RESPONSE" I get "RESP\017ONSE".
>It happens like clockwork, same place, every time. While it COULD be
>the device on the other end (it's 3rd party closed source
>proprietary),
I would almost bet money that it is being inserted by the device
at the other end. I know of nothing else that would cause
that to happen on a repeatable basis in the middle of a word.
>I seriously doubt it. I'm using cfmakeraw to put the port into raw
>mode, and have been playing with the termios settings to no avail.
>
>The closest I could find to the meaning of this character is an
>additional character set code and is controlled by IEXTEN or VDISCARD.
>However setting or clearing either of these has no effect. Does anyone
>have any insight as to what this char is and why I'm seeing it??
SI and SO are control characters used to switch a terminal to an
alternate font and back. Commonly the alternate font will be
block graphics, for example.
man console_codes
>I doubt RS-422 has anything to do with this, I'm sure I'm not
>configuring the serial port properly. Below is the code. You can see
>the various commented-out stuff I've been trying with no luck. Any
>help is greatly appreciated. I'm slowly going crazy.
>
>Thanks for any help.
>Josh
Your posting software line wrapped the code. I've undone everything
I noticed, though it will once again be wrapped by many readers, but
at least it won't get a double dose of the uglies from it. The only
solution for this is to reformat code before you post, and try very
hard to keep lines at less than about 60 columns. That allows it to
be quoted a couple times before it wraps and gets the uglies again.
>/************************************************** ****************************
> Procedure: OpenSerialPort(...)
>
> Purpose: Attempts to open the serial port passed-in as parameter 'port'
>
> Notes: This might also 'exit' and not just 'return' if we could not open our
> serial device!
>************************************************* *****************************/
>int OpenSerialPort(comm_data_t* port,
> const comm_connection_t* environment,
> FILE* logfile)
>{
> int retval = 0; /* our return value */
>
> port->fd = open(port->dev, O_RDWR | O_NOCTTY | O_NDELAY);
> if (port->fd < 0)
> {
> Logger(logfile, "ERROR -- 2 Could not open our serial device", MSG_ERROR);
> perror(port->dev);
> retval = 1;
> port->comm_status = COMM_DOWN;
This code would be a lot clearer if you put the exit here and
skipped the else part of this construct.
if (port->fd < 0)
{
...
exit(1);
}
...
> }
> else
> {
> /* we could open our serial port */
> Logger(logfile," -- Debug: 2 Successfully opened our serial device", MSG_ERROR);
>
> /* if we are using an RF95A the RTS line must be disabled (low) */
> if (environment->RF_MODEM_95A == 1)
> {
> ioctl(port->fd, TIOCMGET, &port->modem_line);
> port->modem_line &= ~TIOCM_RTS;
> ioctl(port->fd, TIOCMSET, &port->modem_line);
> } /* end if */
I'm not sure the above is doing you much good in that position.
You first want to turn off the modem controls and then make sure
RTS is off. Hence, I'd move this to the end of the
configuration.
> tcgetattr(port->fd,&port->ser_oldtio); // save the current modem setting so we can restore them
> tcgetattr(port->fd,&port->ser_newtio); // save the current modem settings to the new settings structure
>
That one is going to byte you badd, sooner or later. You are
assuming that, except for the specific options you are changing
below, the serial port is currently configured correctly. Since
you are _not_ setting every possible option that could affect
how it will work, it is very possible that eventually you'll run
this program after some other program has configured the serial
port, and it won't work.
The classic example is that pppd sets the line discipline to
other than 0, and if it crashes or is killed with SIGKILL, it
does not restore it. Your configuration does not reset that.
Rather than copy the existing configuration to the new struct,
it is far better to just clear the new struct and start from
scratch.
#include <string.h>
memset(&port->ser_newtio, 0, sizeof *port->ser_newtio)
You can then eliminate some of the below code, such as the first
line that clears CRTSCTS, because it is already cleared.
> cfmakeraw(&port->ser_newtio); // set struct for new term settings to raw. See man 'cfmakeraw'
> port->ser_newtio.c_cflag &= ~CRTSCTS; // make sure rts/cts flow control is turned off
>// port->ser_newtio.c_lflag |= IEXTEN;
>// port->ser_newtio.c_cflag |= (CLOCAL | CREAD); // make sure receiver is enabled and port owner not changed
That one is commented out, but with the above you will
definitely need to put it back in! At least the CREAD enabled
part. CLOCAL depends on what you are doing.
>// port->ser_newtio.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG); /*| ECHOE | NOFLSH | ISTRIP); */
>// port->ser_newtio.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | INLCR | ICRNL | IGNCR | /* INPCK | */ ISTRIP | IXON); /* | IXOFF | IXANY); */
>// port->ser_newtio.c_lflag &= ~(CSIZE | PARENB);
>// port->ser_newtio.c_cflag &= ~(CSIZE | PARENB);
>// port->ser_newtio.c_cflag |= CS8;
Put that one back in too.
>// port->ser_newtio.c_oflag &= ~OPOST;
>
> port->ser_newtio.c_cc[VMIN] = 1;
> port->ser_newtio.c_cc[VTIME] = 0;
> //port->ser_newtio.c_cc[VDISCARD] = 0;
>
> cfsetispeed(&port->ser_newtio, port->ser_speed); // set the input baudrate for new terminal settings
> cfsetospeed(&port->ser_newtio, port->ser_speed); // set the output baudrate for new terminal settings
>
> tcflush(port->fd, TCIOFLUSH); // clear the input and output port buffers
> tcsetattr(port->fd,TCSANOW,&port->ser_newtio); // set the new attributes for the serial port immediately
>
> tcgetattr(1,&port->ser_old_stdout_tio); // we gotta do this - there is a Linux bug
What "bug" is that?
> tcsetattr(1,TCSANOW,&port->ser_newtio); // make stdout settings like modem settings
> tcgetattr(0,&port->ser_oldstdtio); // get stdin settings for restoration ...
> tcgetattr(0,&port->ser_newstdtio); // ... and use them as a basis for changes
> tcsetattr(0,TCSANOW,&port->ser_newstdtio); // set the new attributes immediately
> tcsetattr(port->fd,TCSANOW,&port->ser_newtio); // set the new attributes for the serial port immediately
> tcsetattr(port->fd,TCSANOW,&port->ser_newstdtio); // set the new attributes immediately
This got too convoluted to follow!
You have, in order:
1. configured the serial port (ser_newtio)
2. saved stdout's config (ser_old_stdout_tio)
3. config'd stdout using the serial port config (ser_newtio)
4. saved stdin's config (ser_oldstdtio)
5. saved stdin's config (ser_newstdtio)
6. config'd stdin with stdin's config (ser_newstdtio)
7. config'd the serial port, again (ser_newtio)
8. config'd the serial port using stdin's config (ser_newstdtio)
Item 1, you want to keep. I can't believe that any of the rest
of that is useful. In particular, it does no good to set up a
configuration for the serial port, use it for the stdout, while
writing it twice to the serial port and then finally overwriting
it using the stdin config!
(Damned those all night programming sessions! :-)
> tcflush(port->fd, TCIOFLUSH); // clear the input and output port buffers
>
> port->comm_status = COMM_OK; //we really need some error checing on all of the above calls ...
There is *never* any point in writing a comment that you need to
add error checking. Don't waste time with the comment, *add* *the*
*error* *checking*.
You have no idea what happened there, because if any single part
of that has a typo that resulted in a failed function call, the
rest of it is useless.
>
> } /* end else */
>
> if (port->fd < 0)
> {
> Logger(logfile, "ERROR -- 2b Could not open our serial device", MSG_ERROR);
> perror(port->dev);
> exit(-1);
> } /* end if port->fd < 0 */
That should have all been placed up above where the port was
opened, IMHO.
>
> //set the serial port to return immediately after a read
> fcntl(port->fd, F_SETFL, FNDELAY);
> //fcntl(port->fd, F_SETFL, O_NONBLOCK);
I like to separate the open function from the configuration
function, so I'll just copy my entire open function here
for you, just to show a different way of doing the above.
int
open_serial_port(char *port)
{
int fd, oldflags;
/* O_NDELAY allows open w/o carrier detect */
if (-1 != (fd = open(port, O_RDWR | O_NDELAY))) {
/* clear O_NDELAY to allow read() to block */
if ((-1 != (oldflags = fcntl(fd, F_GETFL, 0))) &&
(-1 != fcntl(fd, F_SETFL, oldflags & ~O_NDELAY))) {
/* flush input and output */
if (0 == tcflush(fd, TCIOFLUSH)) {
return fd;
}
}
}
return -1;
}
> return(retval);
>
>} /* end OpenSerialPort */
--
Floyd L. Davidson <http://web.newsguy.com/floyd_davidson>
Ukpeagvik (Barrow, Alaska)
(E-Mail Removed)