Same as with output. Example:

cat /dev/ttyS0

Or:

cat < /dev/ttyS0

The first example is an app that opens the serial port and relays what it reads from it to its stdout (your console). The second is the shell directing the serial port traffic to any app that you like; this particular app then just relays its stdin to its stdout.

To get better visibility into the traffic, you may prefer a hex dump:

od -x < /dev/ttyS0
Answer from Jirka Hanika on Stack Exchange
🌐
nixCraft
cyberciti.biz › nixcraft › howto › linux › how to check and use serial ports under linux
How To Check and Use Serial Ports Under Linux - nixCraft
July 7, 2024 - For example, I am running it using ... UART: unknown, Port: 0x02e8, IRQ: 3 · The setserial with -g option help to find out what physical serial ports your Linux box has....
Discussions

linux - How to open, read, and write from serial port in C? - Stack Overflow
I am a little bit confused about reading and writing to a serial port. I have a USB device in Linux that uses the FTDI USB serial device converter driver. When I plug it in, it creates: /dev/ttyUSB... More on stackoverflow.com
🌐 stackoverflow.com
Reading data from a serial port
Dear List - I am trying to capture data from a serial port and write it to a file. /var/www$ cat /dev/ttyS0 > scale_value.html cat: /dev/ttyS0: Device or resource busy /var/www# cat /proc/tty/driver/serial serinfo:1.0 driver revision: 0: uart:16550A port:000003F8 irq:4 tx:90 rx:270 brk:2 ... More on unix.com
🌐 unix.com
0
0
October 3, 2014
bash - Linux serial port listener and interpreter? - Stack Overflow
One command may use a Windows executable or bat file to save the output to a variable which I pass on to the serial device, and I can use Linux commands (such as wget) on the other line without having to write an entire function to do so in C++. ... while read -r line < /dev/ttyS2; do # $line ... More on stackoverflow.com
🌐 stackoverflow.com
How to read from a Linux serial port - Stack Overflow
I am working on robot which has to control using wireless serial communication. The robot is running on a microcontroller (by burning a .hex file). I want to control it using my Linux (Ubuntu) PC. ... More on stackoverflow.com
🌐 stackoverflow.com
August 18, 2022
🌐
mbedded.ninja
blog.mbedded.ninja › programming › operating-systems › linux › linux-serial-ports-using-c-cpp
Linux Serial Ports Using C/C++ | mbedded.ninja
3 weeks ago - So in any of the above modes, as soon as it has count bytes, read() will return, even if VMIN is set to a higher number or the time specified by VTIME has not yet expired. Rather than use bit fields as with all the other settings, the serial port baud rate is set by calling the functions cfsetispeed() and cfsetospeed(), passing in a pointer to your tty struct and a enum: ... Linux and most BSD systems also provide higher-rate extensions outside POSIX (and a few even higher rates beyond these — B500000, B921600, B1000000, etc., on recent Linux kernels):
Top answer
1 of 2
289

I wrote this a long time ago (from years 1985-1992, with just a few tweaks since then), and just copy and paste the bits needed into each project.

You must call cfmakeraw on a tty obtained from tcgetattr. You cannot zero-out a struct termios, configure it, and then set the tty with tcsetattr. If you use the zero-out method, then you will experience unexplained intermittent failures, especially on the BSDs and OS X. "Unexplained intermittent failures" include hanging in read(3).

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

int
set_interface_attribs (int fd, int speed, int parity)
{
        struct termios tty;
        if (tcgetattr (fd, &tty) != 0)
        {
                error_message ("error %d from tcgetattr", errno);
                return -1;
        }

        cfsetospeed (&tty, speed);
        cfsetispeed (&tty, speed);

        tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;     // 8-bit chars
        // disable IGNBRK for mismatched speed tests; otherwise receive break
        // as \000 chars
        tty.c_iflag &= ~IGNBRK;         // disable break processing
        tty.c_lflag = 0;                // no signaling chars, no echo,
                                        // no canonical processing
        tty.c_oflag = 0;                // no remapping, no delays
        tty.c_cc[VMIN]  = 0;            // read doesn't block
        tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

        tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl

        tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
                                        // enable reading
        tty.c_cflag &= ~(PARENB | PARODD);      // shut off parity
        tty.c_cflag |= parity;
        tty.c_cflag &= ~CSTOPB;
        tty.c_cflag &= ~CRTSCTS;

        if (tcsetattr (fd, TCSANOW, &tty) != 0)
        {
                error_message ("error %d from tcsetattr", errno);
                return -1;
        }
        return 0;
}

void
set_blocking (int fd, int should_block)
{
        struct termios tty;
        memset (&tty, 0, sizeof tty);
        if (tcgetattr (fd, &tty) != 0)
        {
                error_message ("error %d from tggetattr", errno);
                return;
        }

        tty.c_cc[VMIN]  = should_block ? 1 : 0;
        tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

        if (tcsetattr (fd, TCSANOW, &tty) != 0)
                error_message ("error %d setting term attributes", errno);
}


...
char *portname = "/dev/ttyUSB1"
 ...
int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0)
{
        error_message ("error %d opening %s: %s", errno, portname, strerror (errno));
        return;
}

set_interface_attribs (fd, B115200, 0);  // set speed to 115,200 bps, 8n1 (no parity)
set_blocking (fd, 0);                // set no blocking

write (fd, "hello!\n", 7);           // send 7 character greeting

usleep ((7 + 25) * 100);             // sleep enough to transmit the 7 plus
                                     // receive 25:  approx 100 uS per char transmit
char buf [100];
int n = read (fd, buf, sizeof buf);  // read up to 100 characters if ready to read

The values for speed are B115200, B230400, B9600, B19200, B38400, B57600, B1200, B2400, B4800, etc. The values for parity are 0 (meaning no parity), PARENB|PARODD (enable parity and use odd), PARENB (enable parity and use even), PARENB|PARODD|CMSPAR (mark parity), and PARENB|CMSPAR (space parity).

"Blocking" sets whether a read() on the port waits for the specified number of characters to arrive. Setting no blocking means that a read() returns however many characters are available without waiting for more, up to the buffer limit.


Addendum:

CMSPAR is needed only for choosing mark and space parity, which is uncommon. For most applications, it can be omitted. My header file /usr/include/bits/termios.h enables definition of CMSPAR only if the preprocessor symbol __USE_MISC is defined. That definition occurs (in features.h) with

#if defined _BSD_SOURCE || defined _SVID_SOURCE
 #define __USE_MISC     1
#endif

The introductory comments of <features.h> says:

/* These are defined by the user (or the compiler)
   to specify the desired environment:

...
   _BSD_SOURCE          ISO C, POSIX, and 4.3BSD things.
   _SVID_SOURCE         ISO C, POSIX, and SVID things.
...
 */
2 of 2
68

For demo code that conforms to POSIX standard as described in Setting Terminal Modes Properly and Serial Programming Guide for POSIX Operating Systems, the following is offered.
This code should execute correctly using Linux on x86 as well as ARM (or even CRIS) processors.
It's essentially derived from the other answer, but inaccurate and misleading comments have been corrected.

This demo program opens and initializes a serial terminal at 115200 baud for non-canonical mode that is as portable as possible.
The program transmits a hardcoded text string to the other terminal, and delays while the output is performed.
The program then enters an infinite loop to receive and display data from the serial terminal.
By default the received data is displayed as hexadecimal byte values.

To make the program treat the received data as ASCII codes, compile the program with the symbol DISPLAY_STRING, e.g.

 cc -DDISPLAY_STRING demo.c

If the received data is ASCII text (rather than binary data) and you want to read it as lines terminated by the newline character, then see this answer for a sample program.


#define TERMINAL    "/dev/ttyUSB0"

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

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 |= (CLOCAL | CREAD);    /* ignore modem controls */
    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 */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

void set_mincount(int fd, int mcount)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error tcgetattr: %s\n", strerror(errno));
        return;
    }

    tty.c_cc[VMIN] = mcount ? 1 : 0;
    tty.c_cc[VTIME] = 5;        /* half second timer */

    if (tcsetattr(fd, TCSANOW, &tty) < 0)
        printf("Error tcsetattr: %s\n", strerror(errno));
}


int main()
{
    char *portname = TERMINAL;
    int fd;
    int wlen;
    char *xstr = "Hello!\n";
    int xlen = strlen(xstr);

    fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0) {
        printf("Error opening %s: %s\n", portname, strerror(errno));
        return -1;
    }
    /*baudrate 115200, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B115200);
    //set_mincount(fd, 0);                /* set to pure timed read */

    /* simple output */
    wlen = write(fd, xstr, xlen);
    if (wlen != xlen) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }
    tcdrain(fd);    /* delay for output */


    /* simple noncanonical input */
    do {
        unsigned char buf[80];
        int rdlen;

        rdlen = read(fd, buf, sizeof(buf) - 1);
        if (rdlen > 0) {
#ifdef DISPLAY_STRING
            buf[rdlen] = 0;
            printf("Read %d: \"%s\"\n", rdlen, buf);
#else /* display hex */
            unsigned char   *p;
            printf("Read %d:", rdlen);
            for (p = buf; rdlen-- > 0; p++)
                printf(" 0x%x", *p);
            printf("\n");
#endif
        } else if (rdlen < 0) {
            printf("Error from read: %d: %s\n", rdlen, strerror(errno));
        } else {  /* rdlen == 0 */
            printf("Timeout from read\n");
        }               
        /* repeat read to get full message */
    } while (1);
}

For an example of an efficient program that provides buffering of received data yet allows byte-by-byte handing of the input, then see this answer.


🌐
PragmaticLinux
pragmaticlinux.com › home › how to monitor the serial port in linux
How to monitor the serial port in Linux - PragmaticLinux
January 21, 2022 - No matter what method you use for ... as COM1. On Linux it shows up as a teletype terminal (TTY) device such as /dev/ttyS0, /dev/ttyUSB0 or /dev/ttyACM0....
Find elsewhere
🌐
Unix.com
unix.com › applications › electronics
Reading data from a serial port - Electronics - Unix Linux Community
October 3, 2014 - Dear List - I am trying to capture data from a serial port and write it to a file. /var/www$ cat /dev/ttyS0 > scale_value.html cat: /dev/ttyS0: Device or resource busy /var/www# cat /proc/tty/driver/serial serinfo:1.0 driver revision: 0: uart:16550A port:000003F8 irq:4 tx:90 rx:270 brk:2 RTS|CTS|DTR|DSR|CD 1: uart:16550A port:00001C90 irq:17 tx:19 rx:0 CTS|DSR|CD 2: uart:unknown port:000003E8 irq:4 3: uart:unknown port:000002E8 irq:3 /var/www# cat /proc/interrupts CPU0 ...
Top answer
1 of 1
6

Try Minicom first and see what your serial port returns. Install it with sudo apt-get install minicom

You start it as follows (for ttyS0):

sudo minicom -D /dev/ttyS0

You can set the communication parameters from within Minicom (using ctrl-A P), so you're sure that they are correct.

If your device uses a specific protocol, it might need a command to start its communication. So have a look at the user manual.

Maybe your device is set up to use hardware handshaking. If possible, turn it off (at least to start with).

If you can't turn it off, then you will have to set that up as well on your side. In Minicom this is under ctrl-A O and then serial port setup.

I've used Minicom often to debug serial communications and I find it works best.

I've received some more information from the OP:

The laboratory instrument(Cobas C311) uses ASTM protocol. There in the interface , we just need to click "Send to Host" and it sends a bunch of ASTM records. I just need to receive them in a file. Is there any other setting? What is the command to start acquiring data?...and how to save the data in a file?

Chosen the right paramters. Minicom is showing 9600 8N2. Hope it's alright. But receiving nothing - not a single bit. The analyser says, "The instrument transmitted ENQ as a send request,but the Host did not return ACK or NAK within 15 seconds.(Link Timeout)"

The device uses the ASTM Protocol. The device sends the <ENQ> character as a signal that it is ready to start sending data.
This is not something you will be able to receive using minicom or any other terminal program. You will need proper application software that supports this protocol.

You definitely have to read your user manual and check the installation disk (if any) and the manufacturer's website for an application that supports this protocol.
I would be surprised if they don't have application support for this device. If they offer something it is likely to be a Windows application or some example code plus a library.

It is possible to write something yourself, but it won't be easy. There is some Python support for ASTM and there is a Perl script that you could try.

SO also has a post with some information about ASTM..

🌐
Plugable
kb.plugable.com › serial-adapter › how-to-connect-to-a-serial-device-linux
How Do I Connect to a Serial Device in Linux? - Plugable Knowledge Base
May 7, 2026 - Open a terminal and run dmesg | tail to look for the attached converter path, or run ls -tr1 /dev/tty* | tail -n 1 to print the newest created serial adapter. Run screen /dev/ttyUSB0 9600 in the terminal to transmit and read ...
🌐
Erpnext
marketplace.erpnext.com › best-guide › reading-serial-port-data-in-linux-terminal-a-practical-guide-1767646602
Reading Serial Port Data In Linux Terminal: A Practical Guide
January 6, 2026 - On Linux, serial ports are typically represented as device files in the /dev directory. You’ll often see names like /dev/ttyS0, /dev/ttyUSB0, or /dev/ttyACM0. The ttyS devices are usually the traditional serial ports built into your computer ...
🌐
Watermark
papyrus-dokku.watermark.org › noble-files › read-serial-port-data-in-linux-terminal-a-practical-guide-1767646602
Read Serial Port Data In Linux Terminal: A Practical Guide
January 5, 2026 - In the Linux world, serial ports are typically represented as device files under the /dev directory. Common names include /dev/ttyS0, /dev/ttyS1 (for physical serial ports), and /dev/ttyUSB0, /dev/ttyUSB1 (for USB serial adapters).
Top answer
1 of 3
77

I've solved my problems, so I post here the correct code in case someone needs similar stuff.

Open Port

int USB = open( "/dev/ttyUSB0", O_RDWR| O_NOCTTY );

Set parameters

struct termios tty;
struct termios tty_old;
memset (&tty, 0, sizeof tty);

/* Error Handling */
if ( tcgetattr ( USB, &tty ) != 0 ) {
   std::cout << "Error " << errno << " from tcgetattr: " << strerror(errno) << std::endl;
}

/* Save old tty parameters */
tty_old = tty;

/* Set Baud Rate */
cfsetospeed (&tty, (speed_t)B9600);
cfsetispeed (&tty, (speed_t)B9600);

/* Setting other Port Stuff */
tty.c_cflag     &=  ~PARENB;            // Make 8n1
tty.c_cflag     &=  ~CSTOPB;
tty.c_cflag     &=  ~CSIZE;
tty.c_cflag     |=  CS8;

tty.c_cflag     &=  ~CRTSCTS;           // no flow control
tty.c_cc[VMIN]   =  1;                  // read doesn't block
tty.c_cc[VTIME]  =  5;                  // 0.5 seconds read timeout
tty.c_cflag     |=  CREAD | CLOCAL;     // turn on READ & ignore ctrl lines

/* Make raw */
cfmakeraw(&tty);

/* Flush Port, then applies attributes */
tcflush( USB, TCIFLUSH );
if ( tcsetattr ( USB, TCSANOW, &tty ) != 0) {
   std::cout << "Error " << errno << " from tcsetattr" << std::endl;
}

Write

unsigned char cmd[] = "INIT \r";
int n_written = 0,
    spot = 0;

do {
    n_written = write( USB, &cmd[spot], 1 );
    spot += n_written;
} while (cmd[spot-1] != '\r' && n_written > 0);

It was definitely not necessary to write byte per byte, also int n_written = write( USB, cmd, sizeof(cmd) -1) worked fine.

At last, read:

int n = 0,
    spot = 0;
char buf = '\0';

/* Whole response*/
char response[1024];
memset(response, '\0', sizeof response);

do {
    n = read( USB, &buf, 1 );
    sprintf( &response[spot], "%c", buf );
    spot += n;
} while( buf != '\r' && n > 0);

if (n < 0) {
    std::cout << "Error reading: " << strerror(errno) << std::endl;
}
else if (n == 0) {
    std::cout << "Read nothing!" << std::endl;
}
else {
    std::cout << "Response: " << response << std::endl;
}

This one worked for me. Thank you all!

2 of 3
0

Some receivers expect EOL sequence, which is typically two characters \r\n, so try in your code replace the line

unsigned char cmd[] = {'I', 'N', 'I', 'T', ' ', '\r', '\0'};

with

unsigned char cmd[] = "INIT\r\n";

BTW, the above way is probably more efficient. There is no need to quote every character.

🌐
Google Groups
groups.google.com › g › comp.os.linux.development.system › c › fXHq-ijPgIU
Reading serial port with BASH only
Took me about one hour to get it working ;-) OK, lets start by setting the port's parameters: stty -F /dev/ttyS0 ispeed 9600 ospeed 9600 -ignpar cs8 -cstopb -echo · In my case 9600 baud, no parity, one stop bit. Then I wrote the data I need to send into a file (using hexedit) and send the file's contents: ... Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message ... > Tom???? Kavalek wrote: >> I have one big problem with reading serial port in BASH only.
🌐
Serial over Ethernet
serial-over-ethernet.com › linux-serial-port
Linux Serial Port Guide
December 24, 2024 - If it was, it will be listed in the output of this command. Since dmesg lists all of the system’s devices, we will filter the output through grep. It searches text for lines that contain the provided keyword. The names of serial ports on Linux start with “ttyS”, so we can use that as the query.
🌐
LinuxQuestions.org
linuxquestions.org › questions › linux-newbie-8 › reading-from-a-serial-port-4175549464
[SOLVED] Reading from a serial port
Hi, (newbie question alert ) I'm trying to write a c-code that tries to read incoming data on a serial port. At a high level the protocol is like this:
🌐
Reddit
reddit.com › r/ccna › finding out com port for serial cable in linux?
r/ccna on Reddit: Finding out COM port for serial cable in Linux?
October 30, 2019 -

Does anyone know how to find out which COM port a serial cable is on for an Ubuntu machine?

I know in windows it's easily found in the device manager but I'm trying to learn some new skills so my laptop (personal) is now a linuxbox.

🌐
MakeUseOf
makeuseof.com › home › linux › how to connect to a serial console in linux
How to Connect to a Serial Console in Linux
July 26, 2022 - Then when you exit with Exit, you can use the corresponding serial port. ... While on the application main screen, you can return to the configuration screen with Ctrl + A + O, exit the application with Ctrl + A + X, activate the line wrap mode with Ctrl + A + W, and get help with other shortcuts with Ctrl + A + Z. Minicom is generally used by advanced Linux users and therefore, is not recommended for beginners.