How to read temp from DS18B20 using UART board - c++

I bought board like this now i want to connect temperature sensor to this board.
How to read temperature from sensor in c or c++?
I tried to write some code but it won't work. I connect DS18B20 data cabel directly to TXD and RXD pins.
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <cstring>
#include <inttypes.h>
#include <errno.h>
int
set_interface_attribs (int fd, int speed, int parity)
{
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
std::cout<<"error "<<errno<<" from tcgetattr";
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; // ignore break signal
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)
{
std::cout<<"error "<<errno<<" from tcsetattr";
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)
{
std::cout<<"error "<<errno<<" from tggetattr";
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)
std::cout<<"error "<<errno<<" setting term attributes";
}
int main()
{
char *portname = "/dev/ttyUSB0";
int tty_fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
if (tty_fd < 0)
{
std::cout<<"error "<<errno<<" opening "<<portname<<": "<< strerror (errno);
return -1;
}
set_interface_attribs (tty_fd, B9600, 0); // set speed to 115,200 bps, 8n1 (no parity)
set_blocking (tty_fd, true);
unsigned char c = 0xCC;
if(!write(tty_fd, &c, sizeof(c)))
std::cout<<"Write error";
sleep(2);
unsigned char buffer[8];
int size;
if((size = read(tty_fd, &buffer, 8)) < 0)
std::cout<<"Error";
else
std::cout<<"CC("<<size<<")='"<<buffer<<"'";
std::cout<<"\n";
c = 0x44;
if(!write(tty_fd, &c, sizeof(c)))
std::cout<<"Write error2";
c = 0xBE;
if(!write(tty_fd, &c, sizeof(c)))
std::cout<<"Write error2";
sleep(2);
if((size = read(tty_fd, &buffer, 8)) < 0)
std::cout<<"Error";
else
std::cout<<"BE("<<size<<")='"<<buffer<<"'";
std::cout<<"\n######################\n";
close(tty_fd);
}
I got:
CC(1)='Č#'
BE(2)='#ž#'
######################
CC(1)='Č#'
BE(2)='#ž#'
######################
CC(1)='Č#'
BE(2)='#ž#'
######################
Can you help me?

You can't do this with any software. The DS18B20 is electrically incompatible with the board you have. The sensor uses a 1-wire, open-collector communication scheme that is completely different from the serial protocol normally used with this board. With great difficulty you might be able to do some bit-banging of the RTS/CTS signals but you need circuitry to combine them into a bidirectional open-collector signal.

You can hack the UART to communicate with 1 wire protocol.
Connect Rx to Tx and add 4.7 pull-up resistor
See the application note from maxim:
http://www.maximintegrated.com/en/app-notes/index.mvp/id/214

As pointed out by user3804701, it's indeed possible to communicate with a 1-Wire device using a UART interface, and the application note at https://www.maximintegrated.com/en/app-notes/index.mvp/id/214 contains all the information needed to make it work.
But the OP's code needs several fixes:
Each transaction with DS18B20 is made of 3 steps: initialization (also called reset), ROM command and function command, optionally followed by a data exchange
The initialization or reset step is performed by configuring the UART with 9600 bps baud rate, transmitting 0x0F, and receiving a dummy byte; then, the baud rate must be set to 115200 bps to perform the next steps
Following the reset step, data is sent to DS18B20 by writing to the UART a 0xFF byte for each data bit set to 1, and a 0x00 byte for each bit set to 0, starting from the least significant bit; for example, to send 0xAB (i.e. 10101011), one would write to the UART the sequence (FF FF 00 FF 00 FF 00 FF); for each byte written to the UART, there is a "return byte" that needs to be read from the UART and discarded
Data is received from DS18B20 by sending the 0xFF byte following the rules in the previous bullet point, but instead of discarding the "return bytes" reading from the UART a byte for each data bit, starting from the least significant bit: a 0xFF value means that the bit value is 1, otherwise the bit value is 0; for example, the sequence (00 FF FF 00 FF 00 00 FF) read from the UART means that DS18B20 sent 0x96 (i.e. 10010110)
If there is just one DS18B20 connected to the 1-Wire bus, all transactions can use "skip ROM" (byte value 0xCC) as ROM command
The first transaction to be performed is the one that triggers a temperature conversion from DS18B20, with function command 0x44
After waiting for the conversion to complete (which can take up to 750 ms), the host can perform a second transaction to read the DS18B20 scratchpad memory (function command 0xBE); the scratchpad memory is 9 bytes long and contains among other things the temperature value
So in summary the steps needed to get a temperature sample from DS18B20 are: reset, write 0xCC, write 0x44, wait for conversion, reset, write 0xCC, write 0xBE, read 9 bytes.
Example code that implements this stuff is available at https://github.com/dword1511/onewire-over-uart.

Related

Unable to read serial data in Linux C++ :Serial Data getting split when reading via C++

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
}

Reading arduino serial data from C++ in Linux(ubuntu)

My setup is as follows :- OS : LINUX (ubuntu),
Arduino nano + HC-SR04 sensor. The code for which is:-
const int trigPin = 5;
const int echoPin =6;
// defines variables
long duration;
int distance;
void setup() {
pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output
pinMode(echoPin, INPUT); // Sets the echoPin as an Input
Serial.begin(9600); // Starts the serial communication
}
void loop() {
// Clears the trigPin
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
// Sets the trigPin on HIGH state for 10 micro seconds
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// Reads the echoPin, returns the sound wave travel time in microseconds
duration = pulseIn(echoPin, HIGH);
// Calculating the distance
distance= duration*0.034/2;
if (distance <50) {
Serial.print("Chko Distance: ");
Serial.println(distance); }
}
Now I want to read the serial output from C++ , the code for which is :-
// C library headers
#include <stdio.h>
#include <string.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()
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, B9600);
cfsetospeed(&tty, B9600);
// 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
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, '\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!)
printf("Read %i bytes. Received message: %s", num_bytes, read_buf);
close(serial_port);
return 0; // success
}
Code is working fine and all, but its printing either one or several lines from sensor output. Whereas I want just one line to be printed from sensor, which to my limited knowledge seems like the connectivity between arduino code and C++ code isnt proper.
Distance printed twice
┌─[monisha#monisha-asus]─[~/cpp/serial] $./a.out
Read 36 bytes. Received message: Chko Distance: 0
Chko Distance: 0
Distance printed only once
┌─[monisha#monisha-asus]─[~/cpp/serial]$ ./a.out
Read 18 bytes. Received message: Chko Distance: 0
Distance printed several times
┌─[monisha#monisha-asus]─[~/cpp/serial]$ ./a.out
Read 256 bytes. Received message: Chko Distance: 0
Chko Distance: 0
Chko Distance: 0
Chko Distance: 0
Chko Distance: 0
Chko Distance: 0
Please someone help me, thanks Monisha

How to ensure correct data is received, if serial receiver execution time varies

I have a serial communication between STM32 and Linux computer.
STM transmits every 10ms 6 bytes of data "iiiiiC" (i = transmission_counter % 10, C = character 'C').
The receiver however does not have a specific iteration time and may read serial faster or slower than 10ms. Also, the read function should be non-blocking.
This means that sometimes received data:
RD = '111'
RD = '11C2'
RD = '222'
RD = '2C33'
I want usable_data = 22222C
and sometimes:
RD = '333C44444C'
RD = '55555C66666'
RD = 'C77777C8888'
I want usable_data = 77777C
Code is similar to following:
int main() {
int serial_port = open("/dev/ttyACM1", O_RDWR);
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] = 0; // No blocking, return immediately with what is available
tty.c_cc[VMIN] = 0;
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;
}
while (1){
char read_buf [100]; // Large serial buffer so even if the receiver is slow the most recent data will be in this array
memset(&read_buf, '\0', sizeof(read_buf));
int num_bytes = read(serial_port, &read_buf, sizeof(read_buf));
std::cout << num_bytes << " " << read_buf << std::endl;;
... unknown time ahead ...
}
close(serial_port);
return 0; // success
}
I tried some workarounds, but they don't solve all the problems or I'm otherwise not happy with them:
// For cases where receiver is slow and receives more than one complete message at once
char part [6];
uint16_t index = 0;
for (index; index < 6; index++){
if (read_buf[index] == 'C'){
while (read_buf[index] == 'C'){
index += 6;
}
index -= 6;
break;
}
}
memcpy(part, read_buf + index - 5, 6);
.
// For cases where received data does not fit into read_buffer, i.e. we are reading old data - flush it.
int num_bytes = read(serial_port, &read_buf, sizeof(read_buf));
while (num_bytes == sizeof(read_buf)){
num_bytes = read(serial_port, &read_buf, sizeof(read_buf));
}
.
// For cases where received data is out of phase (message does not end with a 'C'). Reads one character at a time until message ends with 'C'. Has many problems.
if (read_buf[sizeof(read_buf)-1] != 'C'){
read(serial_port, &read_buf, 1);
}
but since usable_data might be split into parts (part of usable_data in the current message, part of it in the previous one) it's difficult to merge the correct bytes together.
The best way to handle that specific challenge is to let read() take care of that for you by leveraging the fact that You don't have to point the second argument of read() to the start of the buffer. You can make it write the data from wherever it last stopped.
constexpr std::size_t msg_len = 6;
std::array<char, msg_len> read_buf;
int received = 0;
void poll_port() {
auto count = read(
serial_port,
read_buf.data() + received, // Append to whatever we already have
msg_len - received); // Only read data up to the end of the buffer.
if(count != -1) {
received += count;
if(received == msg_len) {
handle_message(read_buf);
received = 0;
}
}
else if(errno != EAGAIN) {
// we failed for some other reason than lack of data.
}
}
Combine this with configuring the port in non-buffered mode as per https://stackoverflow.com/a/5616108/4442671, to make read() non-blocking, and you'll have pretty much what you need.
Obviously, this is a simplified example that assumes all messages are of the same length and that no data corruption happens along the way, but it should set you on the general path.
How to ensure correct data is received, if serial receiver execution time varies
Write a parser that will parse the incoming data and accumulate them in a buffer and notify "upper processing stage" only when a full packet is received.
bytes of data "iiiiiC"
That's trivially trivial to write, a sample parser might look like the following:
struct myrecv {
std::array<char, 6> buf;
unsigned pos;
// pointer to a function that return EOF or valid character
std::function<int()> readc;
// pointer to callback function to call when full buffer is received
std::function<void(std::array<char, 6>&)> cb;
myrecv(std::function<int()> readc,
std::function<void(std::array<char, 6>&)> cb) :
readc(readc), cb(cb), pos(0) {}
int process() {
buf[pos] = readc();
if (buf[pos] == EOF) return ETIMEDOUT;
pos++;
if (pos != buf.size()) {
return 0;
}
pos = 0;
// check message integrity
if (buf.end() != 'C') {
return -EINVAL;
}
cb(buf);
return 1;
}
};

Reading uart hex values ignores certain bytes/characters

I'm new to C/C++ and I'm trying to make communication by uart using HEX values.
Device port: /dev/ttyS2. Baudrate: 38400
I'm using redis to subscribe the messages. And for testing I'm using "Termite", a RS232 terminal to simulate.
I found some guide that worked really fine, the problem is that when I try to read the message some bytes/characters mess with it.
Here is my code for the connection:
this->fd = open(device,O_RDWR | O_NOCTTY);
/*---------- Setting the Attributes of the serial port using termios structure --------- */
tcgetattr(this->fd, &SerialPortSettings); /* Get the current attributes of the Serial port */
/* Setting the Baud rate */
cfsetispeed(&SerialPortSettings,B38400); /* Set Read Speed as 38400 */
cfsetospeed(&SerialPortSettings,B38400); /* Set Write Speed as 38400 */
/* 8N1 Mode */
SerialPortSettings.c_cflag &= ~PARENB; /* Disables the Parity Enable bit(PARENB),So No Parity */
SerialPortSettings.c_cflag &= ~CSTOPB; /* CSTOPB = 2 Stop bits,here it is cleared so 1 Stop bit */
SerialPortSettings.c_cflag &= ~CSIZE; /* Clears the mask for setting the data size */
SerialPortSettings.c_cflag |= CS8; /* Set the data bits = 8 */
SerialPortSettings.c_cflag &= ~CRTSCTS; /* No Hardware flow Control */
SerialPortSettings.c_cflag |= CREAD | CLOCAL; /* Enable receiver,Ignore Modem Control lines */
SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY); /* Disable XON/XOFF flow control both i/p and o/p */
SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* Non Cannonical mode */
SerialPortSettings.c_oflag &= ~OPOST; /*No Output Processing*/
SerialPortSettings.c_oflag = 0;
/* Setting Time outs */
SerialPortSettings.c_cc[VMIN] = 1; /* Read at least X characters */
SerialPortSettings.c_cc[VTIME] = 0; /* Wait indefinetly */
tcflush(this->fd, TCIFLUSH);
if((tcsetattr(this->fd,TCSANOW,&SerialPortSettings)) != 0) { /* Set the attributes to the termios structure*/
printf("\n ERROR ! in Setting attributes");
}
else {
tcsetattr(fd, TCSANOW, &SerialPortSettings);
printf("\n BaudRate = 38400 \n StopBits = 1 \n Parity = none\n\n");
}
And here is the code for reading:
char read_buffer[32];
int bytes_read;
while(true) {
bytes_read = read(serialport.fd, &read_buffer, sizeof(read_buffer)); /* Read the data */
printf("bytes read: %d", bytes_read);
if (bytes_read < 0) {
printf("Error reading: %s", strerror(errno));
}
else if (bytes_read > 0) {
//read_buffer[bytes_read] = '\0';
printf("\n");
printf("HEX:");
for (int i=0; i<bytes_read; i++) { /*printing only the received characters*/
printf(" %02x",read_buffer[i]);
}
printf("\n");
printf("+----------------------------------+\n");
/*#if defined(MODULE_REDIS) || defined(MODULE_ALL)
//redis.publish("channel:uart:ack", read_buffer);
redis.publish("channel:uart:ack", vectorUint8toHex(read_data).c_str());
#endif*/
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
When I send the message 0x2401010203040506070809af23, it only shows 8 bytes. (the bytes 0x03 and 0x04 messes with the message)
OUTPUT:
bytes read: 0bytes read: 8
HEX: 05 06 07 08 09 af 23 0a
+----------------------------------+
But when I send 0x2401010200000506070809af23, it works fine
OUTPUT:
bytes read: 14
HEX: 24 01 01 02 00 00 05 06 07 08 09 af 23 0a
+----------------------------------+
What I'm doing wrong? And one more thing. Using Termite have to turn the "append LF" setting or else I can't read the message, but that adds "0a" to my message, is this related to the configurations of the serial port?
Ok, so the problem was that some bytes in binary were 0x03 and 0x04 that are in ASCII "end of text" and "end of transmission". And was missing this line on the configurations:
SerialPortSettings.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
Thanks to the comments, I searched for some missing configurations and found it.
There's another post for my problem that I didn't found at the time:
Binary serial port read missing bytes in c

Read 0 bytes after writing to /dev/ttyS0

I have been trying to perform serial communication on linux via the /dev/ttyS devices but when I try to read from them after writing I read no data.
I have the following code
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <unistd.h>
int main() {
printf("hello world\n");
int n;
int fd;
char c;
int bytes;
char buffer[10];
char *bufptr;
int nbytes;
int tries;
int x;
struct termios options;
fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY);
if(fd == -1) {
perror("open_port: Unable to open:");
} else
tcgetattr(fd, &options);
// Set the baudrate, same speed for both I/O
cfsetispeed(&options, B150);
cfsetospeed(&options, B150);
// Enable reading
options.c_cflag |= (CLOCAL | CREAD);
// Set 'RAW' mode
cfmakeraw(&options);
// Set byte size
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
// Set parity
// options.c_cflag &= ~PARENB;
options.c_cflag |= PARENB;
options.c_cflag |= PARODD;
options.c_cflag &= ~CSTOPB;
// Set StopBits, #Linux no OneHalf is supproted, so OneHalf and Two are the same
options.c_cflag &= ~CSTOPB;
// Set Handshake options
// options.c_iflag |= CRTSCTS;
// options.c_iflag &= ~CRTSCTS;
// options.c_cflag &= ~( IXON | IXOFF | IXANY );
options.c_cflag |= IXON | IXOFF | IXANY;
// Set Timeouts
options.c_cc[VMIN] = 0; // read() will return after receiving character
options.c_cc[VTIME] = 10; // == 0 - infinite timeout, != 0 - sets timeout in deciseconds
tcsetattr(fd, TCSANOW, &options);
tcflush(fd, TCIOFLUSH);
bytes = write(fd, "ATZ\r",4);
printf(" wrote %d bytes\n", bytes);
bufptr = buffer;
bytes = read(fd, bufptr, sizeof(buffer));
printf("number of bytes read is %d\n", bytes);
perror ("read error:");
for (x = 0; x < 10 ; x++) {
c = buffer[x];
printf("%d ",c);
}
tcflush(fd, TCIOFLUSH);
close(fd);
printf("\n");
return (0);
}
The program output is as follows
hello world
wrote 4 bytes
number of bytes read is 0
read error:: Success
0 0 0 0 0 0 0 0 0 0
Although I expected it to read the 4 characters I just wrote it seems that read reads 0 bytes. In case i put VTIME to 0 then read blocks forever. I have tried to do echo /dev/ttyS0 but no output comes out. Any idea what might cause this and how can it be fixed?
Your code apparently is ok, except for the following facts:
You call perror("read error"); after calling printf(3), and not right after read(2) so the possible error (if happened) is masked to the call to read(2) by the call to printf(3). If you want to print the number of read characters, save the value of errno and the returned value from read(2) before calling printf(3), and then, if the returned error is neg, then call perror(3).
Anyway. The c_cc[VTIME] = 10 imposes a one second timeout, and this is too sort for resetting a modem. Your line settings are:
CS8, Parity ODD, one STOP bit, and 150 baudrate
Normally, modems answer to ATZ\r command after resetting, which takes some time (frequently more than a second) and in the default modem speed (because you have reset it) and not at the speed at which you have send the AT command.
Resetting a modem is, for this reason, done blindly normally, and then you send a simple AT\r command, in order to ask for an \r\nOK\r\n response. The answer to AT\r command is normally immediate, not as the response to a reset command, and lets the modem adjust its communication settings according to the received chars.
Modems always adapt their speed when the A and T sequence is received, by sampling the square pulses received at a high sampling frequency, then they switch speed to the detected and normally do speed conversion (making it possible to talk to the modem at a different speed than the negotiated remotely)