0

I have an arduino code which prints temperature from MLX90614 sensor, which works fine on arduino serial console.

96.5
96.9
97.1
97.1

But problem arises when I try to read this serial output via a C++ code. Most of the times it gets split in two lines. Can anyone please help me as to whats happening wrong here. I can make out that the data when being printed is unable to keep up with the pace of data sent and hence this behavior but how to actually resolve this I cant figure out as Im a new to C++.

9
7.03 //Split
97.0//Correct
9
6.90 //Split
9
6.91 //Split

My C++ code to read serial data :-

// C library headers
#include <typeinfo>
#include <stdio.h>
#include <string.h>
#include <iostream>
// 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()
using namespace std;

int main() {
  // Open the serial port. Change device path as needed (currently set to an standard FTDI USB-UART cable type device)
  int serial_port = open("/dev/ttyUSB0", O_RDWR);

  // Create new termios struc, we call it 'tty' for convention
  struct termios tty;

  // Read in existing settings, and handle any error
  if(tcgetattr(serial_port, &tty) != 0) {
      printf("Error %i from tcgetattr: %s\n", 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] = 10;    // Wait for up to 1s (10 deciseconds), returning as soon as any data is received.
  tty.c_cc[VMIN] = 0;

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

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

  // Allocate memory for read buffer, set size according to your needs
  unsigned char read_buf[16];
  // 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, '\0', 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));
      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!)

 // while (1) {
 // printf("Read %i bytes. Received message: %s", num_bytes, read_buf);
 // usleep(2000);
 // }



  while (1) {
    int n = read(serial_port, read_buf, sizeof(read_buf) - 1);
    if (n < 0) {
        /* handle errno condition */
        return -1;
    }
    
    //else {
    //cout << n << '\n'; }

    read_buf[n] = '\0';
    std::cout << read_buf<<'\n' ;
    usleep(500);
    if (n == 7) {
    std::cout << "hello" << '\n';
    }

 }
  close(serial_port);
  return 0; // success
}
  • The serial bus is a streaming way of communication. There's no beginnings and no ends to the data, it's just a stream of data. You need to come up with a way to separate the data, by creating a *protocol*. It can be as simple as just using a newline to mark the end of a packet. Then you need to read and buffer all data until you get the newline, when you can process the packet. – Some programmer dude Jul 05 '21 at 10:41
  • As an addendum, because the communication is streaming, a single call to `read` may not only return with less than a full packet (what you call "splitting" the data), it might also return with *more* than a single packet, perhaps even a packet and then a partial packet (again "splitting" the data). You need to be able to handle both too short and too long reads. – Some programmer dude Jul 05 '21 at 10:42
  • In my arduino code ive added S (start) and E(end) , so now output on arduino serial is like S95.34E , S96.11E, S97.14E, S97.14E . Now how to modify C++ code to read entire value starting between S and until E ? – Monisha yadav Jul 05 '21 at 10:54
  • @Someprogrammerdude sir can you please post the answer, Ill accept. Ive been unable to solve this, cant figure out as Im just a beginner in C++ – Monisha yadav Jul 05 '21 at 11:34
  • *"In my arduino code ive added S (start) and E(end) ..."* -- Since you're sending ASCII text, you would be better off transmitting each value as a *line* terminated with a newline, `\n`, character. Then your program can read using canonical mode, and fetch a line of text per **read()**. See https://stackoverflow.com/questions/57152937/canonical-mode-linux-serial-port/57155531#57155531 – sawdust Jul 05 '21 at 15:12
  • @sawdust you sire are great !! Finally Im getting output as --> Read 9: 0x53 0x39 0x36 0x2e 0x36 0x34 0x45 0xd 0xa "S96.64E.." – Monisha yadav Jul 05 '21 at 16:18
  • @sawdust how to convert the unsigned char buffer to float ? I tried bothe xplicit and implicit but none is working float f=(float)(buf) OR float fc=buf ----- > ERROR ==> error: invalid cast from type ‘unsigned char*’ to type ‘float’ – Monisha yadav Jul 05 '21 at 17:32
  • *"how to convert the unsigned char buffer to float ?"* -- You have text in an array. Convert the array to a string, e.g. replace the `\r` and `\n` with a null byte. Remove the `S` and `E` characters from the string, and you have suitable input for **atof()**. See https://stackoverflow.com/questions/7951019/how-to-convert-string-to-float *"Im a new to C++"* -- Then study a C++ or C language guide on (text) strings, and learn the difference & relationship between (char) arrays and strings (and pointers). – sawdust Jul 06 '21 at 23:05

0 Answers0