Why is it that when this ttyUSB is addressed via C, sometimes something is written to stdout and sometimes not?

Hi I have the following code written in c for a ttyUSB device. Some parts of the code are from another source:

// C library headers
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// Linux headers
#include <fcntl.h> // Contains file controls like O_RDWR
#include <errno.h> // Error integer and strerror() function
#include <termios.h> // Contains POSIX terminal control definitions
#include <unistd.h> // write(), read(), close()
// Custom headers
//// klammern
////#include "some_functions.h"

////int write_log(char*);

int main(int argc, char* argv[]) {
  if (argc < 2) {
    printf("Usage: %s <at-command>n", argv[0]);
    exit(-1);
  }
  int serial_port = open("/dev/ttyUSB4", O_RDWR);
  // Check for errors
  if (serial_port < 0) {
      printf("Error %i from open: %sn", errno, strerror(errno));
      exit(1);
  }
  struct termios tty;

  // Read in existing settings, and handle any error
  if(tcgetattr(serial_port, &tty) != 0) {
      printf("Error %i from tcgetattr: %sn", errno, strerror(errno));
      return 1;
  }

  tty.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common)
  tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common)
  tty.c_cflag &= ~CSIZE; // Clear all bits that set the data size
  tty.c_cflag |= CS8; // 8 bits per byte (most common)
  tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common)
  tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1)

  tty.c_lflag &= ~ICANON;
  tty.c_lflag &= ~ECHO; // Disable echo
  tty.c_lflag &= ~ECHOE; // Disable erasure
  tty.c_lflag &= ~ECHONL; // Disable new-line echo
  tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP
  tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl
  tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // Disable any special handling of received bytes

  tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars)
  tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed
  // tty.c_oflag &= ~OXTABS; // Prevent conversion of tabs to spaces (NOT PRESENT ON LINUX)
  // tty.c_oflag &= ~ONOEOT; // Prevent removal of C-d chars (0x004) in output (NOT PRESENT ON LINUX)

  tty.c_cc[VTIME] = 50;    // Wait for up to 1s (10 deciseconds), returning as soon as any data is received.; 5 sek ^= 50
  tty.c_cc[VMIN] = 0;

  // Set in/out baud rate to be 9600
  cfsetispeed(&tty, B9600);
  cfsetospeed(&tty, B9600);

  // Save tty settings, also checking for error
  if (tcsetattr(serial_port, TCSANOW, &tty) != 0) {
      printf("Error %i from tcsetattr: %sn", errno, strerror(errno));
      return 1;
  }

  // Write to serial port
  //unsigned char msg[] = { 'a', 't', '+', 'c', 'g', 'p', 'a', 'd', 'd', 'r', '=', '1', 'r' };
  unsigned int argv1_len = strlen(argv[1]);
  char msg[argv1_len+2]; // unsigned char
  strcpy(msg, argv[1]); 
  ////write_log(msg);
  strcat(msg, "r");
  
  write(serial_port, msg, sizeof(msg));
  
  // Allocate memory for read buffer, set size according to your needs
  char read_buf [256];

  // Normally you wouldn't do this memset() call, but since we will just receive
  // ASCII data for this example, we'll set everything to 0 so we can
  // call printf() easily.
  memset(&read_buf, '', sizeof(read_buf));

  // Read bytes. The behaviour of read() (e.g. does it block?,
  // how long does it block for?) depends on the configuration
  // settings above, specifically VMIN and VTIME
  int num_bytes = read(serial_port, &read_buf, sizeof(read_buf));

  // n is the number of bytes read. n may be 0 if no bytes were received, and can also be -1 to signal an error.
  if (num_bytes < 0) {
      printf("Error reading: %s", strerror(errno));
      close(serial_port);
      return 1;
  }else if (num_bytes == 0) {
      printf("0");
      close(serial_port);
      return 1;
  }

  // Here we assume we received ASCII data, but you might be sending raw bytes (in that case, don't try and
  // print it to the screen like this!)
  //printf("Read %i bytes. Received message: %s", num_bytes, read_buf);
  printf("%s", read_buf);
  fflush(stdout); 
 
  ////removeLeadingNewlines(read_buf);
  
  ////write_log(read_buf);

  close(serial_port);
  return 0; // success
}

And this is my Makefile, it builds with no errors or warnings:

WARNFLAGS = -W -Wall -Werror
OPTFLAGS = -O3
DEBUGFLAGS = -ggdb3 -DDEBUG
CFLAGS += $(WARNFLAGS)
binaries = at_commander

ifdef DEBUG
  CFLAGS += $(DEBUGFLAGS)
else
  CFLAGS += $(OPTFLAGS)
endif

all: $(binaries)

at_commander: some_functions.c

clean:
    $(RM) *~ $(binaries) *.o

The question is why what is now being output to the console irregularly:

root@OpenMPTCProuter:~/autostart# ./at_commander at
root@OpenMPTCProuter:~/autostart# ./at_commander at
root@OpenMPTCProuter:~/autostart# ./at_commander at
root@OpenMPTCProuter:~/autostart# ./at_commander at

OK
root@OpenMPTCProuter:~/autostart# ./at_commander at
root@OpenMPTCProuter:~/autostart# ./at_commander at
root@OpenMPTCProuter:~/autostart# ./at_commander at

OK
root@OpenMPTCProuter:~/autostart# ./at_commander at

OK
root@OpenMPTCProuter:~/autostart# ./at_commander at
root@OpenMPTCProuter:~/autostart# ./at_commander at
root@OpenMPTCProuter:~/autostart# ./at_commander at

OK
root@OpenMPTCProuter:~/autostart#

First I thought it has something to do with flushing, but maybe it has something to do with wrong timing in my code.

Thank you in advance!

13

Why is it that when this ttyUSB is addressed via C, …

A ttyUSB device” is not an end-point device (such as a flash drive), but rather a communications interface. Presumably you have something connected to this “ttyUSB device” using a cable, and your intent is to communicate with that connected device. However you completely neglect to mention anything about any other device.

… sometimes something is written to stdout and sometimes not?

Your use of the noun “something” is ambiguous and completely disregards the fact that this involves (a) a command message is transmitted by your program, (b) that message has to be received and processed by a remote unit, (c) a response message has to be transmitted by that remote unit, and (d) that response message has to be received/read by your program.
In other words that “something” is a response from a remote unit/device, and the failure (of your program) to “sometimes not” indicates that all of the required steps with that remote unit/device in that sequence were not successful .

There are only a few clues in your post to identify the connected device that your program is trying to communicate with. One is a commented-out initialization. There are references to “at-command“. So apparently the connected device is a modem that uses the AT (aka Hayes) command set.


Your program as posted has a variety of minor issues, most of which have been mentioned in comments. IMO the salient bug in your program is expecting to receive the entire response when using a noncanonical read() with a timeout.

Your program configures the serial terminal to noncanonical mode using the assignment

  tty.c_lflag &= ~ICANON;

The two termios attributes that control when and what a read() will return are assigned the following (note the discrepancy between the actual value and the comment):

  tty.c_cc[VTIME] = 50;    // Wait for up to 1s (10 deciseconds), returning as soon as any data is received.; 5 sek ^= 50
  tty.c_cc[VMIN] = 0;

Per the termios man page, you have setup a read with timeout:

   MIN == 0, TIME > 0 (read with timeout)
         TIME specifies the limit for a timer in tenths of a second.  The timer is started when read(2) is called.  read(2) returns  either  when  at
         least  one byte of data is available, or when the timer expires.  If the timer expires without any input becoming available, read(2) returns
         0.  If data is already available at the time of the call to read(2), the call behaves as though the data was received immediately after  the
         call.

The salient point is that the timeout will occur only when no data is available. This configuration does not mean that read() will wait for the VTIME duration, and then return with all available data. Rather, as soon as at least one byte is available, the read() could return (with just that single byte).

Your program’s results indicates that the full response from the modem is the string rnOKrn. That is six character consisting of CR, LF,’O’, ‘K’, CR, and LF. This is indicated on the “good” output of a blank line after the command, the OK text, and your shell prompt showing up on a new line. Omit any of the mentioned control characters, and you would then see a different “good” output.

When your program “sometimes not” display a response, the
read() probably returned with only the first character of the six-byte response. When your program prints that first character of the response, you do not notice it because (1) it’s a control character (i.e. carriage return) rather than a printable character, and (2) the cursor is already at the first column, so another CR does nothing.

To verify this hypothesis, revise the print statement to be more informative and distinctive (as @Gerhardh suggested in his comment).

-   printf("%s", read_buf);
+   printf("received %d: '%s'n", num_bytes, read_buf);


To read the entire response try

  tty.c_cc[VTIME] = 2;    // rtn after 0.2 sec of idle after first char
  tty.c_cc[VMIN] = 80;

2

What do you mean with The question is why what is now being output to the console irregularly?

A modem sends an OK answer preceded and followed by CR LF characters (a pair of characters) so what you receive in the buffer is rnOKrn, which prints as you show, leavind a blank line before the ok. This is after you have detected that you read something, use the following statement to convert your data buffer into a printable buffer.

But you have assumed (erroneously) that the buffer you have read is printable with printf(), using a %s format specifier. That is wrong, you can print only null delimited strings, and the read() system call will not put the nul character for you.

You have an easy solution for that:

  1. specify one value less (to allow space to later put a nul character yourself, after the data) when calling read (use sizeof buffer - 1, instead of sizeof buffer)
  2. put a nul char in the position of the last character read (read_buff[num_bytes] = 0;).

You will print a blank line, followed by OK and a second new line, making your output as you see it.

What is wrong with what you observe? That’s the effect of printing a new line, OK and a new newline. And that is what you get.

BTW there’s no flushing when you deal with read() or write(). Only when you use printf() you are subject to flushing. But on a terminal, printf flushes all output as soon as it sees a n character in the parameter list, so it should not be an issue.

6

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật