I'm developing a receiver for a small hardware project. I'm working on a small board that uses UART to transfer data.
The receiver code is shown in full below, I'll explain the problematic bits separately shortly.
#define TTY "/dev/ttys002"
#include <stdio.h>
#include <string.h>
#include <unistd.h> //Unix standard functions
#include <fcntl.h> //File controls
#include <errno.h> //Error numbers
#include <assert.h>
#include <termios.h> //Posix terminal
int open_port(const char * tty) {
int fd;
fd = open(tty, (O_RDWR | O_NOCTTY | O_NDELAY));
assert("__failed to open port__" && fd != -1);
//Block until read
fcntl(fd, F_SETFL, 0);
return fd;
}
void configure_port(int fd) {
struct termios options;
//Get current options
tcgetattr(fd, &options);
//9600 Baud
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
//Receive & local mode
options.c_cflag |= (CLOCAL | CREAD);
//Raw output
options.c_oflag &= ~OPOST;
//No hardware flow control
options.c_cflag &= ~CRTSCTS;
//No parity, 1 stop bit
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
//8 data bits
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
//Write options back, set them immediately
tcsetattr(fd, TCSANOW, &options);
}
int main(int argc, const char * argv[]) {
int fd = open_port(TTY);
const size_t count = 8;
char buf[count + 1];
ssize_t n;
configure_port(fd);
while (1) {
n = read(fd, buf, count);
buf[count] = '\0';
if (n > 0) {
printf("%s\n", buf);
}
}
return 0;
}
Since I don't have my hardware at hand currently, I decided to test my receiver over a regular tty (#define TTY "/dev/ttys002"
). To test it, I simply compiled and ran the above code and then opened a separate terminal and:
echo "text" >> /dev/ttys002
All this works fine and well and I get all the data I'm echoing into the tty.
However, the problem arises when I input a long message into the tty:
echo "this is a longer test message" >> /dev/ttys002
I receive the whole message as a single string in my program output. Why is this so? I would have expected the text to be split up into blocks of 8 chars (const size_t count = 8;
).
If it's of importance, I'm using this guide as my go-to for the configuration.
Edit: Please see the comments for further discussion on the issue.
IMHO, your message is split into blocks of eight chars : n = read(fd, buf, count);
cannot give more than count bytes at a time.
But as you do not configure the tty line in RAW mode, is is still in line buffered mode. So the underlying driver blocks the first read until it has a full line terminated with a \n
(or exceeding the buffer capacity).
Then the read returns with the first 8 bytes, and next reads immediately return with 8 bytes too since data is available in driver buffer.
You should look at non canonical mode in man termios, if you want to use the raw input mode :
options.c_cflags &= ~ICANON;
/* optionally if defaults are not appropriates */
options.c_cc[VMIN] = vmin; /* default is 1 */
options.c_cc[VTIME] = vtime; /* default is 0 */
But anyway, you read chararacters and never add the terminating null, so buf has no reason to be null terminated. You should only print the number of characters actually read:
const size_t count = 8;
char buf[count];
ssize_t n;
configure_port(fd);
while (1) {
n = read(fd, buf, count);
if (n > 0) {
printf("%.*s\n", n, buf); /* only print the n characters */
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With