0

I have problem with reading data from serial port on linux.I configure port using termios library and receive data from microcontroller which work perfectly (I checked that using hterm program) .When i send "e" character to microcontroller it should send me sequence of characters in hex "77 31 31 32 33 34 a " "77 32 35 36 37 a" and "77 33 38 39 a" only once but it send me it multiple times. Part of code which write data to microcontroller works well. method responsible for reading

whole code

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

int Terminal::configPort(int fd, int sp){
    struct termios tty;
    tcgetattr(fd, &tty);
    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);
    tty.c_cflag |= (CLOCAL | CREAD);
    tty.c_cflag &= ~PARENB;                     
    tty.c_cflag &= ~CSTOPB;                     
    tty.c_cflag &= ~CSIZE;                      
    tty.c_cflag |= CS8;                         
    tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); 
    tty.c_oflag &= ~OPOST;                      
    tty.c_cc[VMIN] = 0;                         
    tty.c_cc[VTIME] = 240;      
tcsetattr(fd, TCSANOW, &tty);}

int Terminal::openPort(char* p){
    int f;                                          
    f = open(p, O_RDWR | O_NOCTTY | O_NDELAY);  
    if (f == -1) {
        printf("Error opening %s: %s\n",p, strerror(errno));
    } else {
        fcntl(f, F_SETFL, 0);}                                                      
    return(f);
}
Terminal::Terminal(char* p ,int s){
    port=p;
    speed=s;
}
void Terminal::readData(){
    unsigned char buf[80];
    int rdlen;
    rdlen = read(fd, buf, sizeof(buf) - 1);
    if (rdlen > 0) {
       unsigned char   *p;     
        for  ( p = buf; rdlen > 0; ++p, --rdlen )
            {   printf(" %x", *p);}
        printf("\n");} 
    else if (rdlen < 0) {
            printf("Error from read: %d: %s\n", rdlen, strerror(errno));
    } else {  
            printf("Timeout from read\n");
    }     
}

Terminal::~Terminal(){
    close(fd);  
}

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

class Terminal{

char* port;
int speed;

public:
int fd;
int configPort(int,int);
int openPort(char*);
void readData();
Terminal(char* ,int);
~Terminal();
};
#include <errno.h>
#include <fcntl.h> 
#include <string.h>
#include <termios.h>
#include <stdio.h>  
#include <unistd.h>
#include <stdlib.h>
#include "Terminal.h"



int main(){
    char *portname = "/dev/ttyACM0";   
    int written;
    Terminal w(portname,B115200);
    w.fd=w.openPort(portname);
    w.configPort(w.fd,B115200);
    char message[] = "e";
    int messageSize = strlen(message);
    written = write(w.fd, message, messageSize);
    printf("\nTotal bytes written: %d\n", written);
    sleep(3);
    w.readData();
}

expected output:

77 31 31 32 33 34 a 77 32 35 36 37 a 77 33 38 39 a

Real output: longer than expected

36 37 a 77 33 38 39 a 77 31 31 32 33 34 a 77 32 35 36 37 a 77 33 38 39 a 77 31 31 32 33 34

or shorter

 77 31 31 32 33

Thanks in advance for any help.

sawdust
  • 16,103
  • 3
  • 40
  • 50
  • You need to show the code that sends the "e" and calls `readData()`. And I would change the for statement to: `for ( p = buf; rdlen > 0; ++p, --rdlen )`. – Jim Rhodes Sep 01 '22 at 18:32
  • changing for statement didn't help. – dawid gurdzinski Sep 01 '22 at 19:09
  • Your [mre] cannot be compiled because is not C but C++, and you have typos in it. Please copy-and-paste your actual code. -- Aside from this minor issues I see no reason why your PC software should print multiple times. – the busybee Sep 01 '22 at 19:23
  • Your termios configuration is incomplete, and therefore left to chance. Your program is susceptible to this issue: https://stackoverflow.com/questions/73344616/in-linux-cr-are-unexpectedly-converted-to-lf-when-icrnl-flag-is-set-and-icanon – sawdust Sep 02 '22 at 19:49

1 Answers1

0

Real output: longer than expected ... or shorter

That's a summation rather than a description of what happens.
Does a "short" response occur before the "long" response?

The non-canonical read() that your program uses is not guaranteed to return with your definition or concept of a message/packet. Since VMIN is set to zero, any available bytes in the termios buffer is sufficient to satisfy this read request, and then the VTIME value becomes irrelevant.
When the read() returns less bytes than what you expect for a message, then another read() should be performed to fetch the remainder.

But your program only performs a single read() to fetch the entire response from the microcontroller. That will only work if that entire response has already been received, and is residing in the termios buffer when the read() is serviced (for VMIN=0, VTIME>0).
Otherwise your program's read() will return with a "short" response. The remainder of this response will be returned to your program on the next read(), which may combine this "previous" response with some portion of the "current" response.

There is no "false read" as claimed in your title. Rather, your program's reads are not synchronized with the received messages. Whenever you get an "incomplete" read() (with respect to the expected response), your program will get "stale" data on the next read().


expected output:

 77 31 31 32 33 34 a 77 32 35 36 37 a 77 33 38 39 a

Since the expected response from the microcontroller consists of three lines of text, the simple and preferred solution would use canonical mode rather than non-canonical mode for reading. A working termios configuration is presented in this answer.
Your program would have to call read() three times, once for each line of text.

sawdust
  • 16,103
  • 3
  • 40
  • 50