18

I'm trying to talk to a device using a usb-to-serial converter on linux. Drivers are working, the device exists at /dev/ttyUSB0.

There is a strange problem (checked with a null-modem cable to another computer): Whatever program I use to connect to the device (putty, minicom, screen), they all send a CR (\r) on enter. I (and my device) expect a LF (\n) on enter.

When I use the console to send a command - it sends a LF:

# echo Hello World > /dev/ttyUSB0

Results in "Hello World\n". I also wrote the classic c hello world program (printf("Hello, world!\n");) and redirected the output to /dev/ttyUSB0 - also gives me a "Hello world\n" on the other end of the line.

But all other terminal programs send a \r on enter.

What's going on here?

3
  • 7
    Standard TTY protocol is to sent a CR from the keyboard when the user has finished typing and for the computer to echo LF when it's ready for the next line. Commented Jun 26, 2014 at 17:27
  • 5
    Check the current settings of your tty with stty -a. Use stty -ocrnl to change the output behaviour of CR being translated to LF.
    – ott--
    Commented Jan 18, 2016 at 16:41
  • I tried all kind of options, nothing changed the behaviour. Really strange. Anyway it's an old problem that I currently do not care for (anymore).
    – ChristophK
    Commented Feb 17, 2020 at 10:59

6 Answers 6

8

I guess by “enter” you mean the Return key of your keyboard, probably labeled .

Short answer:

The reason why putty, minicom and screen send CR when you hit Return is because the Return key actually means “Carriage return” (CR).

The reason why echo and printf("Hello, world!\n"); send LF is because they end their output with \n, which means LF. Note that these programs do not hit a Return key: they do not even touch a keyboard.

Long answer:

Back in the seventies, we were transitioning from ttys to CRT-based computer terminals. Early terminals had keyboards with separate keys for CR and LF. On the DEC VT05, these were labeled, quite appropriately, CR and LF. On the VT50 and VT100 series, they were labeled RETURN and LINE FEED. Later models dropped the LINE FEED key and we ended up with Return and no key for LF. Nowadays, these bulky terminals have been replaced by terminal emulators such as rxvt or gnome-terminal. However, as the name implies, a terminal emulator's job is… to emulate a terminal! Thus, the situation hasn't changed that much: the Return key still stands for CR, just as it did in 1975.

On the other hand, Unix standardized on using LF as the end-of-line indicator. Both in text files and in user-space programs, lines of text are always terminated by LF. This begs the question: how do we then interact with a Unix system using a terminal that makes sending LF a lot harder than sending CR? The answer is… it depends on what specific program we are interacting with.

When we interact with a “naive” program, or rather a program that doesn't bother to handle the terminal, the in-kernel terminal driver is in “canonical” (or “cooked”) mode. In this mode, it translates the CR characters coming from the terminal to LF, and the outgoing LFs to CR+LF sequences. These translations can be seen as the icrnl and onlcr flags in the output of stty -a.

Some programs prefer to handle the terminal themselves: they instruct the terminal driver to refrain from doing character conversions (they set it to “raw” mode) and handle these conversions themselves. Most often this is done within a library such as readline or ncurses. Bash is one of those programs (it uses readline) but, thankfully, it resets the terminal to cooked mode before exec()-ing other programs.

Now, imagine you want to talk to some sort of device that speaks text on a serial port. You go to your attic and grab your old VT420 that has been collecting dust there for almost 30 years. You hook it to your device using a cross-over (“null-modem”) cable, and voilà! Your device has a terminal! How cool is that? Hopefully, this device has been designed to play nicely with a classic terminal: it expects CR-terminated messages. If it expected LF instead, then you would have to end your lines by typing Ctrl-J, as the VT420 has no dedicated LINE FEED key.

If you don't own any of these lovely vintage terminals, you can use instead an emulated one such as gnome-terminal or cool-retro-term. You connect it to your device using a communication program such as minicom, or picocom, and a USB-to-serial converter. In their default configuration, these communication programs do not mess with your data stream: they configure the tty driver in raw mode (on both ends), and they forward the bytes faithfully between your emulated terminal and your device, just like the null-modem cable would do with your VT420. But unlike a null-modem cable, picocom can be configured to translate the line endings on the fly if needed.

1
  • Really nice & elaborate answer. Thank you!
    – ChristophK
    Commented Mar 21, 2023 at 18:58
14

If using picocom, it's a matter of mapping the serial output from CR to CR+LF.

Example:

picocom /dev/ttyS0 --baud 19200 --omap crcrlf --echo

Actually able to send commands to the device now!

2
  • 2
    I was looking for a general explanation / solution. As I mentioned in the question, I'm able to write my own program in C that works (does send a LF). Anyway, your answer might be helpful for someone else ...
    – ChristophK
    Commented Feb 17, 2020 at 11:01
  • 1
    Thanks! I had to use crlf instead of crcrlf so that it converts \r to \n, not \r\n. Commented Aug 25, 2020 at 19:44
5

Please ensure you have set the ttyUSBx port in raw mode. Otherwise there can be some character replacements e.g. \r with \n and vice versa on receiver side.

3
  • 3
    For anyone from an internet search, to do this, just add raw in the settings. e.g. stty -F /dev/USB0 115200 raw
    – TechnoSam
    Commented Dec 20, 2018 at 17:09
  • 2
    I tried it, didn't work.
    – ChristophK
    Commented Feb 17, 2020 at 10:57
  • 1
    The answer is, simply, wrong.
    – MRule
    Commented Oct 6, 2021 at 8:18
1

TLDR: you can't; Screen will override any options set via stty when it starts. It doesn't support the ocrnl/onlcr/icrnl/inlcr options required to configure carriage return vs. newline. It opens the terminal device in exclusive mode, so you can't use stty to change these options after screen starts. This is true for Screen version 4.08.00 (GNU), 05-Feb-20.

Long form:

I've run into this same issue, and have searched through several related questions and answers across the stack exchange universe. I'm forced to conclude that You can't get screen to send Line Feed (LF; \n) instead of Carriage Return (CR; \r), at least not without changing something in the source and recompiling.

This question asks how to send LF instead of CR from screen. It doesn't have an answer yet. This answer is wrong (stty -F /dev/YOURSERIALDEVICE YOURBAUDRATE raw won't change how screen configures the tty). This answer suggests using picocom, but doesn't answer the question of how to get screen to send the correct newline character. It does, however, work:

picocom /dev/YOURSERIALDEVICE --baud YOURBAUDRATE --omap crcrlf --echo
# (press Control+a Control+x to exit)

There are hints for how one might address this for screen in the stty man page. Screen forwards the comma-delimited list of options after the tty file to stty, so some of these options might help:

> man stty
# (output abridged..)
Input settings:
       [-]icrnl    translate carriage return to newline
       [-]inlcr    translate newline to carriage return
Output settings:
       [-]ocrnl    translate carriage return to newline
       [-]onlcr    translate newline to carriage return-newline
       [-]onlret   newline performs a carriage return

However, setting both input and output to replace \r with \n, ensuring that \n isn't replaced by \r, and ensuring that onlret is not set ... doesn't make a difference,

screen /dev/YOURSERIALDEVICE YOURBAUDRATE,ocrnl,-onlcr,icrnl,-inlcr,-onlret

behaves the same as before. I'm not sure why screen doesn't seem to apply the options. This answer to a vaguely similar question suggests using stty to change the options.

> stty -F /dev/YOURSERIALDEVICE YOURBAUDRATE ocrnl -onlcr icrnl -inlcr -onlret
> screen /dev/YOURSERIALDEVICE

This does nothing, because screen resets any configuration performed by stty when it starts. This answer claims that one can run stty after screen starts, but when I do this I get the error Device or resource busy.

This answer also addresses the issue with CR/LF in screen. It refers to the Window Types section of screen's online documentation.

  • "an exclusive open is attempted on the node to mark the connection line as busy" -- so we shouldn't be able to change the tty options after starting screen.

  • Only baud_rate, cs8/cs7 (number of bits per byte), ixon/off, and istrip are documented options. This probably means that ocrnl etc. aren't recognized/supported by screen.

In summary: The question of how to get screen to send LF (\n) instead of CR (\r) has been asked several times on various stack-exchange sites. None of the provided answers work, and a thorough reading of the documentation suggests that it's not possible at this time.

1
  • 1
    The question and problem ist NOT about screen. That was just mentioned as an example program which exhibits the problem – just like many other programs do. Your answer seems kinda close to the point, but actually misses it completely.
    – ChristophK
    Commented Dec 2, 2022 at 10:15
1

I know this is old but it has frustrated me for long enough. As @MRule mentioned, this issue doesn't seem to be solved with any of the given answers here. A similar issue on Unix & Linux exchange had a better answer. So, this might help in case anyone lands here in the future.

python-pyserial comes with its miniterm which seems to do "the right thing" with cr and lf. Should be available in your distro's repository or on pypi.

pyserial-miniterm /dev/ttyUSB0 115200
0

You should use:

echo -e "Hello World\n" > /dev/ttyUSBx

where x={0,1,...n}

The -e is used to send (i.e. interpretation of) special characters

Check help on Ubuntu 18.04 terminal:

~$ help echo

echo: echo [-neE] [arg ...]
    Write arguments to the standard output.
    
    Display the ARGs, separated by a single space character and followed by a
    newline, on the standard output.
    
    Options:
      -n    do not append a newline
      -e    enable interpretation of the following backslash escapes
      -E    explicitly suppress interpretation of backslash escapes
(...)
1
  • 2
    Hello JohnBR, thanks for taking your time to suggest a solution, but you missed the point. Please read my question (and other peoples questions) carefully. "echo" does exactly what I expect (send LF aka '\n'), so there's no need to add any option here. All the other programs are "misbehaving", not echo. Usually I'd give a downvote in this case, but since you're a new contributor I don't.
    – ChristophK
    Commented Feb 17, 2020 at 10:56

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .