Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Some questions on writing data to a serial port?

818 views
Skip to first unread message

Jef Driesen

unread,
Sep 13, 2007, 12:00:29 PM9/13/07
to
For one of my applications, I'm writing a small library for the
communication with a device attached to a serial port. The library is
very simple (open, close, configure, read, write) and is only a small
wrapper around the platform specific (termios, win32) API. Almost
everything works, but I'm having some problems with writing data.

Under linux, I'm using this code for writing:

int serial_write (serial_t fd, const void* buffer, unsigned int count)
{
int nbytes = 0;
while (nbytes < count) {
int n = write (fd, buffer + nbytes, count - nbytes);
if (n < 0)
return -1; // Error during write call.

nbytes += n;
}

return nbytes;
}

While testing (with a virtual device provided by ttypatch [1] and a real
serial port without a device attached), I noticed the write *always*
succeeds (immediately after the first write call). Is this the expected
behavior when there is nothing receiving the data at the other end? I
suppose the data is buffered somewhere (by the OS or the serial driver),
but not actually send. Because sometimes ioctl (fd, TIOCOUTQ, &bytes)
shows there is data waiting in the output queue and close (fd) takes a
long time to finish in that case. If I add tcdrain (fd) before returning
in serial_write, it never returns. I would expect write to return an
error instead of the behavior I described. Or am I doing something wrong?

[1] http://ttypatch.sourceforge.net/

If I compare with the windows version (see code below), the function
blocks until everything is written (optionally a timeout can be
configured). But if the function returns successfully, the output queue
is always empty, just as I expected.

BTW, is it possible to implement a timeout when writing data, similar to
windows? Orherwise my application would become unresponsive (when using
tcdrain) if there is no device present.

int serial_write (serial_t fd, const void* buffer, unsigned int count)
{
DWORD dwWritten = 0;
if (!WriteFile (serial, buffer, count, &dwWritten, NULL))
return -1;

return dwWritten;
}

Barry Margolin

unread,
Sep 13, 2007, 9:33:16 PM9/13/07
to
In article <fcbmqu$hkq$1...@ikaria.belnet.be>,
Jef Driesen <jefdr...@hotmail.com.invalid> wrote:

> For one of my applications, I'm writing a small library for the
> communication with a device attached to a serial port. The library is
> very simple (open, close, configure, read, write) and is only a small
> wrapper around the platform specific (termios, win32) API. Almost
> everything works, but I'm having some problems with writing data.
>
> Under linux, I'm using this code for writing:
>
> int serial_write (serial_t fd, const void* buffer, unsigned int count)
> {
> int nbytes = 0;
> while (nbytes < count) {
> int n = write (fd, buffer + nbytes, count - nbytes);
> if (n < 0)
> return -1; // Error during write call.
>
> nbytes += n;
> }
>
> return nbytes;
> }
>
> While testing (with a virtual device provided by ttypatch [1] and a real
> serial port without a device attached), I noticed the write *always*
> succeeds (immediately after the first write call). Is this the expected
> behavior when there is nothing receiving the data at the other end? I

Yes.

> suppose the data is buffered somewhere (by the OS or the serial driver),
> but not actually send. Because sometimes ioctl (fd, TIOCOUTQ, &bytes)

That's the reason. The kernel buffers writes to most types of devices.
For example, if you try to write to a network file system, it may
succeed even when the server is down, because it simply writes to the
buffer cache, which is flushed to the server asynchronously.

> shows there is data waiting in the output queue and close (fd) takes a
> long time to finish in that case. If I add tcdrain (fd) before returning
> in serial_write, it never returns. I would expect write to return an
> error instead of the behavior I described. Or am I doing something wrong?
>
> [1] http://ttypatch.sourceforge.net/
>
> If I compare with the windows version (see code below), the function
> blocks until everything is written (optionally a timeout can be
> configured). But if the function returns successfully, the output queue
> is always empty, just as I expected.
>
> BTW, is it possible to implement a timeout when writing data, similar to
> windows? Orherwise my application would become unresponsive (when using
> tcdrain) if there is no device present.

You could put the descriptor in non-blocking mode. Then if you try to
write while the buffer is full, you'll get an error (errno ==
EWOULDBLOCK). You can then use select() to wait for the buffer to
drain, with a timeout.

>
> int serial_write (serial_t fd, const void* buffer, unsigned int count)
> {
> DWORD dwWritten = 0;
> if (!WriteFile (serial, buffer, count, &dwWritten, NULL))
> return -1;
>
> return dwWritten;
> }

--
Barry Margolin, bar...@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***

David Schwartz

unread,
Sep 14, 2007, 5:13:28 AM9/14/07
to
On Sep 13, 6:33 pm, Barry Margolin <bar...@alum.mit.edu> wrote:
> In article <fcbmqu$hk...@ikaria.belnet.be>,
> Jef Driesen <jefdrie...@hotmail.com.invalid> wrote:

> > While testing (with a virtual device provided by ttypatch [1] and a real
> > serial port without a device attached), I noticed the write *always*
> > succeeds (immediately after the first write call). Is this the expected
> > behavior when there is nothing receiving the data at the other end? I

> Yes.

> > suppose the data is buffered somewhere (by the OS or the serial driver),
> > but not actually send. Because sometimes ioctl (fd, TIOCOUTQ, &bytes)

> That's the reason. The kernel buffers writes to most types of devices.
> For example, if you try to write to a network file system, it may
> succeed even when the server is down, because it simply writes to the
> buffer cache, which is flushed to the server asynchronously.

If there's no hardware flow control, you can keep writing forever even
if there is no device connected. The data will continue to be output
on the serial port's output lines and there will be no way to tell
that nothing is listening.

DS

Message has been deleted

Barry Margolin

unread,
Sep 14, 2007, 9:11:28 PM9/14/07
to
In article <fcdldn$mpk$1...@ikaria.belnet.be>,
Jef Driesen <jefdr...@hotmail.com.invalid> wrote:

> Barry Margolin wrote:
> > In article <fcbmqu$hkq$1...@ikaria.belnet.be>,
> > Jef Driesen <jefdr...@hotmail.com.invalid> wrote:
> >
> >> For one of my applications, I'm writing a small library for the
> >> communication with a device attached to a serial port. The library is
> >> very simple (open, close, configure, read, write) and is only a small
> >> wrapper around the platform specific (termios, win32) API. Almost
> >> everything works, but I'm having some problems with writing data.
> >>
> >> Under linux, I'm using this code for writing:
> >>
> >> int serial_write (serial_t fd, const void* buffer, unsigned int count)
> >> {
> >> int nbytes = 0;
> >> while (nbytes < count) {
> >> int n = write (fd, buffer + nbytes, count - nbytes);
> >> if (n < 0)
> >> return -1; // Error during write call.
> >>
> >> nbytes += n;
> >> }
> >>
> >> return nbytes;
> >> }
> >>
> >> While testing (with a virtual device provided by ttypatch [1] and a real
> >> serial port without a device attached), I noticed the write *always*
> >> succeeds (immediately after the first write call). Is this the expected
> >> behavior when there is nothing receiving the data at the other end?
> >

> > Yes.
>
> Does that also mean, the while loop is not necessary? I have a similar
> loop for reading, because I use select and a non-blocking read (by
> setting both termios cc[VMIN] and cc[VTIME] to zero, but not using
> O_NONBLOCK) to implement a timeout [1]. But if I understand it
> correctly, this loop is not required for a normal blocking write?
>
> [1] See my topic "Recalculate timeout after select() when reading from
> serial port?" from 7 Sep 2007.
> http://groups.google.be/group/comp.unix.programmer/browse_thread/thread/4bf551
> 5bf7ba577c/ac12dc4f9f6827ae
>
>
> >> I suppose the data is buffered somewhere (by the OS or the serial driver),
> >> but not actually send.
> >

> > That's the reason. The kernel buffers writes to most types of devices.
> > For example, if you try to write to a network file system, it may
> > succeed even when the server is down, because it simply writes to the
> > buffer cache, which is flushed to the server asynchronously.
>

> But how do I detect errors if the data is always send asynchronously and
> write does not return an error? Or is this something I don't need to
> worry about?

What kind of error can occur on a serial port?

If it's a dialup line and the modem hangs up, you'll get an error when
you try to write something after that. But if it was connected when you
called write(), the call will succeed and put the data in the buffer.
While it's possible that the line could hang up between the time your
data is buffered and when it actually gets sent, this is usually such a
short time that no one worries about it. You'll get an error the next
time you write().

>
> >> Because sometimes ioctl (fd, TIOCOUTQ, &bytes)

> >> shows there is data waiting in the output queue and close (fd) takes a
> >> long time to finish in that case. If I add tcdrain (fd) before returning
> >> in serial_write, it never returns. I would expect write to return an
> >> error instead of the behavior I described. Or am I doing something wrong?
> >>
> >> [1] http://ttypatch.sourceforge.net/
> >>
> >> If I compare with the windows version (see code below), the function
> >> blocks until everything is written (optionally a timeout can be
> >> configured). But if the function returns successfully, the output queue
> >> is always empty, just as I expected.
> >>
> >> BTW, is it possible to implement a timeout when writing data, similar to
> >> windows? Orherwise my application would become unresponsive (when using
> >> tcdrain) if there is no device present.
> >
> > You could put the descriptor in non-blocking mode. Then if you try to
> > write while the buffer is full, you'll get an error (errno ==
> > EWOULDBLOCK). You can then use select() to wait for the buffer to
> > drain, with a timeout.
>

> The non-blocking mode would only make a difference when the buffer is
> full, right? Because when there is enough space left, even a blocking
> write does not block. How large is this buffer?

Implementation dependent.

>
> I suppose the implementation of the timeouts with select is the same as
> I did for reading (see simplified code below), or do I have to try
> writing first before the select call?

No, you don't. select() tells you whether or not a call to write would
have blocked. Your pseudo-code is right.

>
> int serial_write (int fd, const void* buffer, unsigned int count)


> int nbytes = 0;
> while (nbytes < count) {

> int rc = select (fd + 1, NULL, &fds, NULL, &tv);
> if (rc < 0) {
> return -1; // Error during select call.
> } else if (rc == 0)
> break; // Timeout.


>
> int n = write (fd, buffer + nbytes, count - nbytes);
> if (n < 0) {
> return -1; // Error during write call.
>
> nbytes += n;
> }
> return nbytes;
> }

--

Jef Driesen

unread,
Sep 15, 2007, 2:30:24 PM9/15/07
to

If the data is send asynchronously from the kernel buffer, does a read
(at a later time) waits until all the data is transmitted? I'm asking
because the communication protocol consist of sending commands and
reading the reply. But if the command is not entirely send to device at
the time I try to read the reply, the device doesn't know yet it has to
send its reply. Does the kernel take care of this or is this why there
is the tcdrain () function?

Gordon Burditt

unread,
Sep 15, 2007, 7:30:20 PM9/15/07
to
>If the data is send asynchronously from the kernel buffer, does a read
>(at a later time) waits until all the data is transmitted? I'm asking

I certainly hope not. Doing that on a busy serial line doing PPP
networking would kill throughput and probably result in buffer
overflows. It might also play havoc with someone trying to type
quickly using a screen-oriented editor like vi or emacs.

>because the communication protocol consist of sending commands and
>reading the reply. But if the command is not entirely send to device at
>the time I try to read the reply, the device doesn't know yet it has to
>send its reply.

So? The device can send its reply after it DOES know it has to
send one. If this is a blocking read, the program will wait. If
it's a non-blocking read, the program have to try again, which will
likely happen anyway because the device does not reply instantaneously
(and even if it did, serial ports aren't instantaneous).

>Does the kernel take care of this or is this why there
>is the tcdrain () function?

I don't understand why you have to wait for the command to be sent
before waiting for the reply, instead of just waiting for the reply?

If you are worried about ignoring the echo of the command you sent,
(does the device echo commands you send it?) you still have to worry
about it anyway. The last couple of characters of the echoed command
will be received after the command has been sent anyway.

Barry Margolin

unread,
Sep 15, 2007, 10:34:03 PM9/15/07
to
In article <4xVGi.106936$t83.5...@phobos.telenet-ops.be>,
Jef Driesen <jefdr...@hotmail.com.invalid> wrote:

A read() will wait until there's something to read. If the data hasn't
been transmitted, the device won't reply, so there won't be anything to
read, and read() will block. When the kernel finishes transmitting
everything the device will reply, and read() will then return the reply.

Jef Driesen

unread,
Sep 16, 2007, 4:55:00 AM9/16/07
to
Gordon Burditt wrote:
>> If the data is send asynchronously from the kernel buffer, does a read
>> (at a later time) waits until all the data is transmitted? I'm asking
>
> I certainly hope not. Doing that on a busy serial line doing PPP
> networking would kill throughput and probably result in buffer
> overflows. It might also play havoc with someone trying to type
> quickly using a screen-oriented editor like vi or emacs.
>
>> because the communication protocol consist of sending commands and
>> reading the reply. But if the command is not entirely send to device at
>> the time I try to read the reply, the device doesn't know yet it has to
>> send its reply.
>
> So? The device can send its reply after it DOES know it has to
> send one. If this is a blocking read, the program will wait. If
> it's a non-blocking read, the program have to try again, which will
> likely happen anyway because the device does not reply instantaneously
> (and even if it did, serial ports aren't instantaneous).
>
>> Does the kernel take care of this or is this why there
>> is the tcdrain () function?
>
> I don't understand why you have to wait for the command to be sent
> before waiting for the reply, instead of just waiting for the reply?

The main problem is I don't have much experience with serial
communication, so I'm probably seeing problems where there are none. But
I'm starting to understand the differences with normal i/o now.

> If you are worried about ignoring the echo of the command you sent,
> (does the device echo commands you send it?) you still have to worry
> about it anyway. The last couple of characters of the echoed command
> will be received after the command has been sent anyway.

Some of the devices send an echo, but not all of them.

sharad...@gmail.com

unread,
May 7, 2014, 3:59:20 AM5/7/14
to
Hi, I have somewhat same problem. I am writing a command to a serial port and expecting a reply. Tcdrain returns 0 so I assume the the command was sent. But I don't see the device responding. What do I do ?

DavidR

unread,
May 7, 2014, 12:13:12 PM5/7/14
to

>
> Hi, I have somewhat same problem. I am writing a command to a serial port and expecting a reply. Tcdrain returns 0 so I assume the the command was sent. But I don't see the device responding. What do I do ?
>
Are you running as root as there may be a permissions issue? Worth a
try and see if that works which would rule out permissions or not.

Scott Lurndal

unread,
May 7, 2014, 1:23:32 PM5/7/14
to
Possibly a baudrate mismatch, if the device isn't replying.

Joe Pfeiffer

unread,
May 7, 2014, 1:59:52 PM5/7/14
to
(note that you're responding to a post from 2007. Its likely the person
who posted it isn't around any more).

sharad...@gmail.com writes:
>
> Hi, I have somewhat same problem. I am writing a command to a serial
> port and expecting a reply. Tcdrain returns 0 so I assume the the
> command was sent. But I don't see the device responding. What do I do
> ?

Get a breakout box and put it between your computer and your device.
Here's an example of what I'm talking about --

http://www.trianglecables.com/rsbreakboxmo.html?utm_source=rsbreakboxmo&utm_medium=shopping%2Bengine&utm_campaign=FROOG&gclid=CN7Z7sCwmr4CFVQiMgodgm4AxQ
(I don't have the particular box shown on that web page; it's just a cheap one
that turned up in a google search)

You can watch the lights flash and see if data is going; you can turn
on/off control lines. It's a tremendously valuable tool to figure out
whether the problem is with sending data (no lights flash), the device
isn't answering (only the transmit light flashes), or the problem is
with receiving data (send and receive both flash, but you aren't seeing
data).

Something I keep thinking would be a tremendously valuable tool would be
to build a "soft" breakout box out of two serial ports on a PC (well,
two USB-serial adapters these days!) -- in addition to performing a lot
of the functionality of a hardware box, it could act as a
special-purpose logic analyzer watching logic pulses, and do things like
autodetect transfer rates.

Not quite valuable enough to have actually written it, and of course there
are fewer serial devices in service every day...

Joe Pfeiffer

unread,
May 7, 2014, 2:03:00 PM5/7/14
to
Ah, good point -- make sure you're checking the returns on all your
calls, like to open() and write(). You mention tcdrain() returns 0, so
I'd expect your other calls are also working (I think if you pass an fd
of -1 to tcdrain you ought to get an error, but best to check anyway).

James K. Lowden

unread,
May 8, 2014, 12:14:13 AM5/8/14
to
On Wed, 7 May 2014 00:59:20 -0700 (PDT)
sharad...@gmail.com wrote:

> > While testing (with a virtual device provided by ttypatch [1] and a
> > real serial port without a device attached), I noticed the write
> > *always* succeeds (immediately after the first write call). Is this
> > the expected behavior when there is nothing receiving the data at
> > the other end?

http://www.cmrr.umn.edu/~strupp/serial.html

The question of "nothing receiving" is almost a koan. If a device
raises DCD and the Unix host transmits a byte by raising and lowering
the voltage on pin 2, is it really sent?

The model here is that open returns when the device indicates, by
raising certain lines on the port, that it is ready to receive. Once
that happens, it's presumed ready to receive (!) and write(2) should
Just Work. If the device becomes unready for some reason, the OS
buffers a few bytes and send them when it comes back up or, if enough
bytes are written, blocks the writer.

> > BTW, is it possible to implement a timeout when writing data,
> > similar to windows? Orherwise my application would become
> > unresponsive (when using tcdrain) if there is no device present.

You can use select(2) for a timeout. Timing out on open(2) is
trickier. Traditionally you would use alarm(3) to send a signal to
interrupt the open call.

HTH.

--jkl

Gordon Burditt

unread,
May 9, 2014, 10:24:06 AM5/9/14
to
> Hi, I have somewhat same problem. I am writing a command to a
> serial port and expecting a reply. Tcdrain returns 0 so I assume
> the the command was sent. But I don't see the device responding.
> What do I do ?

What's a command? Is it a line of ASCII text terminated by a \r\n (like SMTP,
NNTP, POP3, and AT modem-dialing commands, only over a serial port, not TCP)
or is it some binary format?

Some things to check:

- Do the baud rates match? Sending at 57600 bps and receiving
at 1200bps may not generate any received characters at all,
especially if there are long pauses between characters or the
command is short.

- Make sure you do not have getty running on the serial port
you are trying to use to talk to the device. There's nothing
like two listeners on the same serial port to screw up
the conversation.

- The tty driver tends to do unexpected things to the data stream
if you aren't careful about setting all the ioctl() flags. Does
your application depend on a \n line ending? It might be getting
\r\n instead. Are you sure you aren't sending 5-bit characters?
Or 9-bit characters including parity, which may be even worse?
(normally you want 8-bits no-parity or 7-bits even-parity or
7-bits odd-parity. In any case you probably want data bits +
parity bits = 8 bits).

- You have to get the presence or absence of a crossover cable
right. Otherwise you have both ends transmitting at each other
on the same wire, and both ends listening on the other wire.
Fortunately, RS-232 is designed so both ends driving the same
wire in opposite directions doesn't cause fires. Those RS-232
breakout boxes which you don't see much any more could indicate
which end was driving which lines.

- If you don't at least wire DCD high or set ioctl() bits to ignore
it, open() will wait for it to come up and send SIGHUP if it goes
low and the serial port is the control terminal.

- Make sure you are not echoing what the device sends back into the
device. Even worse is echoing what the device sends back into
the device, which echos it back into the serial port, sometimes
seen when a serial port and modem both have echo on. (Your
complaint is getting nothing, not getting a continuous stream of
jibberish, though).

Generally, detecting a device on the other end of a serial line is
not easy, especially if you fake out the control lines by, say,
jumpering DTR to DCD at the serial port rather than letting the
device supply DCD if and only if it is powered on. It's sorta like
getting the President to shut up when the last person watching his
televised press conference turns off the TV or changes the channel.
The best detection is to send the device something that provokes a
response, e.g. a (nuclear) self-destruct command. (and base a
timeout on that response).

Try connecting a terminal (nowadays, a PC with a USB serial port),
and run a terminal program (e.g. tip or cu if the PC is running
Linux, or maybe Terminal on Windows. Someone who uses Windows a
lot probably has a better alternative for that OS). See if you can
ship some ASCII out and see it on the screen. Manually type a
response.

Do it the other way and play computer with the PC connected to the
device, and see if you can provoke a response manually without your
computer running the program being connected at all. Remember, the
device might need to see a control line active (typically DCD or
DTR) before it will pay attention to incoming characters.


0 new messages