Blocking read until specified number bytes arrived - c

I have a requirement of waiting using "read" until a buffer is full on an audio codec device. For make it easier, lets take similar example of:
fd= read(fileno(stdin), &buf, 10);
How can I return from the read when I type 10 characters in stdin? (I hope if this is success, I can wait on codec until specified bytes of data is arrived).
The above example needs an "Enter Key" from console, where as I want "read" to unblock only when desired bytes of data is arrived.
EDIT: Requirement is waiting using a single "read" till specified bytes are arrived.

How about this:
#define BUFFER_SIZE 10
char buffer[BUFFER_SIZE];
/* ... */
size_t total_read = 0;
size_t total_left = BUFFER_SIZE; /* The total size of the buffer */
char *buffer_pointer = buffer; /* buffer is where to store the data */
while (total_left > 0)
{
ssize_t current = read(STDIN_FILENO, buffer_pointer, total_left);
if (current <= 0)
{
/* Error or end of file */
if (current < 0)
perror("read"); /* Error! */
break;
}
else
{
total_read += current; /* We have read some more data */
total_left -= current; /* Less data left to read */
buffer_pointer += current; /* So we don't read over already read data */
}
}
printf("Received %ld characters\n", total_read);
for (unsigned int i = 0; i < total_read; i++)
printf("Character #%d: '%c'\n", i, buffer[i]);
Beware that this will block your program until all data is read. The amount of characters read may be less than everything, because there might be an error or the user pressed CTRL-D (end of file).
Also note that the STDIN_FILE file-descriptor is most likely connected to a tty, which means it might be buffered, so doesn't return data until newline, and that you might have to make the tty unbuffered.
Edit
To make sure the tty connected to stdin is unbuffered, use the following code:
#include <termios.h>
/* ... */
/* Somewhere before reading from stdin... */
struct termios tty_settings;
tcgetattr(STDIN_FILENO, &tty_settings);
tty_settings.c_lflag &= ~ICANON;
tcsetattr(STDIN_FILENO, TCSANOW, &tty_settings);
For more information about the tcsetattr function and the ICANON flag, check the manual page for tcsetattr.

do {ioctl(fd_port, FIONREAD, &bytes);} while (bytes < size);
May help, before issuing a read().

Related

How to send raw binary data over serial in C without non-native libraries in linux

I'm currently trying to send raw binary data in the format of decimal to an external device over serial. I currently have the data in a buffer array but would like it in a structure like this:
struct packetData{
uint8_t sync1;
uint8_t sync2;
uint16_t messageId;
uint16_t dataWordCount;
uint16_t flags;
uint16_t checksum;
};
I'm also using 9600 baud, and have all the termios settings set using cfmakeraw and I'm currently writing using:
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
int flags = O_RDWR | O_NOCTTY | O_NDELAY;
fd = open(device, flags);
uint16_t buf_tx[BUFFER_SIZE] = {255,129,191,0,2057,0};
if(fd == -1){
printf("\n Failed to open port! ");
return -1;
}
tcgetattr(fd, &tty); //Get the current attributes of the Serial port
cfmakeraw(&tty);
cfsetispeed(&tty, B9600); //Set read speed as 9600 baud
cfsetospeed(&tty, B9600); //Set write speed as 9600 baud
if((tcsetattr(fd, TCSANOW, &tty)) != 0){
printf("Error! Can't set attributes.\n");
return -1;
}
else{
printf("Connection successful! \n");
}
while(x < 1000){
memset(buf_tx, 0, sizeof(buf_tx));
tcflush(fd, TCOFLUSH);
if(y < 5){
if(write(fd, buf_tx, 5) == -1){
printf("\n");
printf("Error>>: %s\n",strerror(errno));
y++;
}
}
tcflush(fd, TCIOFLUSH);
usleep(1000);
x++;
}
This code isnt the full code, just the setup/write parts so no need to worry about its syntax. if possible it would be nice not to have that buffer array and just use the struct directly, but I'll take what I can get.
It seems you have the serial port opening more or less in hand. I prefer to set the termios member components explicitly myself, but cfmakeraw() is perfectly fine too.
What you should consider, is having a separate function to send one or more of those structures at a time. For example,
int write_all(const int fd, const void *buf, const size_t len)
{
const char *data = buf;
size_t written = 0;
ssize_t n;
while (written < len) {
n = write(fd, data + written, len - written);
if (n > 0) {
written += n;
} else
if (n != -1) {
/* C library bug, should never occur */
errno = EIO;
return -1;
} else {
/* Error; n == -1, so errno is already set. */
return -1;
}
}
/* Success. */
return 0;
}
The function will return 0 if all data was successfully written, and -1 with errno set if an error occurs.
To send a struct packetData pkt; just use write_all(fd, &pkt, sizeof pkt).
To send a full array struct packetData pkts[5]; use write_all(fd, pkts, sizeof pkts).
To send n packets starting at pkts[i], use write_all(fd, pkts + i, n * sizeof pkts[0]).
However, you do not want to use tcflush(). It does not do what you think it does; it actually just discards data.
Instead, to ensure that the data you have written has been transmitted, you need to use tcdrain(fd).
I recommend against adding tcdrain(fd) at the end of write_all() function, because it blocks, pauses the program, until the data has been transmitted. This means that you should only use tcdrain() before you do something that requires the other end has received the transmission; for example before trying to read the response.
However, if this is a query-response interface, and you do intend to also read from the serial device, you should set tty.c_cc[VMIN] and tty.c_cc[VTIME] to reflect how you intend to use the interface. I prefer asynchronous full-duplex operation, but that requires select()/poll() handling. For half-duplex, with these exact structures only, you can use tty.c_cc[VMIN] = sizeof (struct packetData) with say tty.c_cc[VTIME] = 30, which causes read() to try and wait until a full structure is available, but at most 30 deciseconds (3.0 seconds). Something like tty.c_cc[VMIN] = 1; tty.c_cc[VTIME] = 1; is more common; that causes read() to return a short count (even 0!) if there is no additional data received within a decisecond (0.1 seconds). Then, the receive function could be along the following lines:
int read_all(const int fd, void *buf, const size_t len)
{
char *const ptr = buf;
size_t have = 0;
ssize_t n;
/* This function is to be used with half-duplex query-response protocol,
so make sure we have transmitted everything before trying to
receive a response. Also assumes c_cc[VTIME] is properly set for
both the first byte of the response, and interbyte response interval
in deciseconds. */
tcdrain(fd);
while (have < len) {
n = read(fd, ptr + have, len - have);
if (n > 0) {
have += n;
} else
if (n == 0) {
/* Timeout or disconnect */
errno = ETIMEDOUT;
return -1;
} else
if (n != -1) {
/* C library bug, should never occur */
errno = EIO;
return -1;
} else {
/* Read error; errno set by read(). */
return -1;
}
}
/* Success; no errors. */
return 0;
}
If this returns -1 with errno == ETIMEDOUT, the other side took too long to answer. There may be remainder of the late response in the buffer, which you can discard with tcflush(TCIFLUSH) (or with tcflush(TCIOFLUSH), which also discards any written data not yet transmitted). Synchronization in this case is a bit difficult, because the above read_all() function doesn't return how many bytes it received (and therefore how many bytes to discard of a partial structure).
Sometimes the interface used always returns the number of bytes, but also sets errno (to 0 if no error occurred, and a nonzero error constant otherwise). That would be better for a query-response interface read and write functions, but many programmers find this use case "odd", even though it is perfectly okay by POSIX.1 standard (which is the relevant standard here).

How to handle buffering serial data

I am trying to figure out a nice solution to reading serial data, and what to do when a read() is done but it contains an incomplete message.
The expected messages between devices have a defined start and end byte so its easy to see when a message starts and ends.
I can open a serial port fine and read from the serial port. But I am encountering the computer is reading faster than data coming through and I get an incomplete message.
For this example, lets say the message expected is
0x10 0xFF 0xFF 0xFF 0xFF 0x11
With 0x10 the start, 0x11 the end, and 0xFF is the data bytes
I am new to C so I may be missing something obvious,
My current solution
int main() {
/* Ommited serial port opening and checking*/
char read_buffer[80];
char message_buffer[80];
int message_buffer_index = 0;
int start_index = -1;
int end_index = -1;
int read_bytes;
read_bytes = read(serial_port, read_buffer, sizeof(read_buffer) - 1);
/* Now lets say read_bytes returns 3 and read buffer is {0x10, 0xFF, 0xFF} */
/* What should I do with the read_buffer? Currently appending to message buffer*/
memcpy(&message_buffer[message_buffer_index], &read_buffer[0], read_bytes);
/* Now check the message buffer for a full message */
for (int i = 0; i < 80; i++) {
if (message_buffer[i] = 0x10) {
start_index = i;
continue;
}
if (message_buffer[i] = 0x11) {
end_index = i;
}
if (start_index != -1 && end_index != -1) {
/* Found a message, do something with it, not super important here */
process_message();
/* Now how to erase the full message from the
buffer and push any non processed data to the
front? */
remove_message();
}
}
}
int process_message();
int remove_message();
To minimize the overhead of making many read() syscalls of small byte counts (e.g. the misguided solution of reading a byte at a time), use an intermediate buffer in your code.
The read() of the serial terminal should be in blocking mode to avoid a return code of zero bytes.
#define BLEN 1024
unsigned char rbuf[BLEN];
unsigned char *rp = &rbuf[BLEN];
int bufcnt = 0;
/* get a byte from intermediate buffer of serial terminal */
static unsigned char getbyte(void)
{
if ((rp - rbuf) >= bufcnt) {
/* buffer needs refill */
bufcnt = read(fd, rbuf, BLEN);
if (bufcnt <= 0) {
/* report error, then abort */
}
rp = rbuf;
}
return *rp++;
}
For proper termios initialization code for the serial terminal, see this answer. You should increase the VMIN parameter to something closer to the BLEN value or at least the length of longest expected message, and a VTIME of 1.
Now you can conveniently access the received data a byte at a time with minimal performance penalty.
#define MLEN 1024 /* choose appropriate value for message protocol */
int main()
{
unsigned char mesg[MLEN];
...
while (1) {
while (getbyte() != 0x10)
/* discard data until start found */ ;
length = 0;
while ((mesg[length] = getbyte()) != 0x11) {
/* accumulate data until end found */
length++;
}
/* process the message */
...
} /* loop for next message */
...
}
Note that your detection for a message frame is not robust.
If the data is binary and therefore can use the same values as these start and end bytes, then this parsing of the received data is prone to misaligned message frames.
See this answer for a description of a proper alogrithm.
You need circular buffer. Place data in the buffer and the process takes them when for example there is enough data or in any convenient moment.
Wikipedia has excellent article about it https://en.wikipedia.org/wiki/Circular_buffer
Better use stdio for reading, something like this:
FILE *fp = fdopen(serial_port, "r");
while (blabla) {
while (fgetc(fp) != 0x10)
; // wait until start
while ((c = fgetc(fp)) != 0x11)
message_buffer[message_buffer_index++] = c;
// here you have a complete message
}
Insert checks for EOF and errors if needed

Using sleep() when reading from a serial port

I am writing an application to read data from /dev/ttyUSB0.
I found it necessary to call sleep before calling read in the while loop so that I get the entire line at once. Otherwise, sometimes I get part of the line and the rest in the next iteration.
Do I have to package my data with a header containing the length of the string being sent over? Or is there a better way?
while(1) {
usleep(10000);
unsigned char buf[80];
int rdlen;
ioctl(fd, FIONREAD, &rdlen);
if (rdlen > 0) {
rdlen = read(fd, buf, rdlen);
}
if (rdlen > 0) {
...
}
The better way is to simply deal with receiving partial lines, have your code reading the data figure out when you have a complete line if that is important.
something like (not tested at all):
char buffer[1000];
size_t offset = 0;
while(1 ){
int len = read(fd, buffer+offset,(unsigned)(sizeof(buffer)-offset));
if(!strchr(buffer+offset, '\n')){
/* no end-of-line */
offset +=len;
}
else
{
/* deal with complete line */
offset = 0;
}
}

Entire characters in the text file is not getting transmitted in Hi 3520D

I am trying to read some data from a text file and writing it to the ttyUSB* socket id.
I am using Hi3520d Dvr. I have it's RS485 port connected to a "RS485 to RS232 converter". This converter is connected to the PC through a USB port.
The text file is getting read properly to the buffer, but while writing last few lines of the text is not transmitting. This is happening with file with size more than 4.5kb exactly and without usleep() function.
I am using minicom on linux terminal to display both read and written text.
Thanks in advance for looking into this.
#include <stdio.h>
#include <termios.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#define Bdrate B9600
int Intserial(char *dev, int Baudrate)
{
//printf("Insterial func\n");
int sid;
int iDebug = -1;
struct termios serial_struct;
sid = open(dev, O_RDWR | O_NOCTTY | O_NDELAY);
if (sid > 0)
{
memset(&serial_struct, 0, sizeof(serial_struct)); /* clear the new struct */
serial_struct.c_cflag = Baudrate | CS8 | CLOCAL | CREAD;
serial_struct.c_iflag = IGNPAR;
serial_struct.c_oflag = 0;
serial_struct.c_lflag = 0;
serial_struct.c_cc[VMIN] = 0; /* block untill n bytes are received */
serial_struct.c_cc[VTIME] = 0; /* block untill a timer expires (n * 100 mSec.) */
iDebug = tcsetattr(sid, TCSANOW, &serial_struct);
if (iDebug < 0)
{
printf("Err 0\n"); //Unable to set serial port parameters
return (0);
}
}
else
{
printf("Err 1\n"); //Serial port not open
return (0);
}
//printf("sid is %d \n",sid);
return (sid);
}
int main()
{
int sid1 = -1, size = 0, i = 0, x, w;
size_t ln;
FILE *fd;
char buf[2233];
fd = fopen("h.txt", "r");
if (fd)
{
sid1 = Intserial("/dev/ttyAMA1", Bdrate); //RS485 port of Hi3520d
if (sid1 > -1)
{
system("himm 0x200F004C 0"); // commands transmitting and recieving
system("himm 0x201A0400 1");
system("himm 0x201a0004 1");
while (!feof(fd))
{
memset(buf, 0, sizeof(buf));
fread(buf, sizeof(buf), 1, fd);
printf("%s", buf);
write(sid1, buf, sizeof(buf));
usleep(5);
}
getchar();
}
else
printf("com port cant open\r\n ");
fclose(fd);
close(sid1);
}
else
printf("File cant open\r\n");
printf("task completed............\r\n");
}
You have to observe return value of fread for number of bytes read by fread function. the actual read size may not equal to bytes requested, also you have to pass number of bytes read by fread (as valid bytes in buffer) to write function as number of bytes to write.
The code should be something like this
memset(buf,0,sizeof(buf));
size_t bytesRead = fread(buf,sizeof(buf),1,fd);
if(bytesRead > 0)
write(sid1,buf, bytesRead);
Also as LPs said, fread doesn't end buffer with termination character, so passing buffer filled by fread to printf("%s") will be undefined behavior
There are numerous issues with your code, but the salient cause of "the text is not transmitting" is probably the failure to check the return value of
write(sid1, buf, sizeof(buf));
Because the serial terminal was opened in non-blocking mode, each write() will return immediately, before the data has been actually transmitted.
Since the serial terminal is configured for a rather slow 9600 baud, the data could be queued up in the line discipline buffer and other intermediate buffers.
The line discipline buffer is typically 4096 bytes long.
Assuming that the fread() operations are always successful (which you seem to have verified), then the second iteration of the write() of 2233 bytes could potentially saturate the line discipline buffer, and return with a short write return value (which would be ignored).
The third iteration of the write(), if it's quick enough, could then be outright rejected with a return value of -1 and an errno of EAGAIN to indicate that the write would block.
This error condition would be silently ignored, and this 2233 bytes of data will never be transmitted.
This seems to correlate perfectly with your observation of "last few lines of the text is not transmitting ... with file with size more than 4.5kb exactly and without usleep() function."
ADDENDUM
Revised code for blocking mode, proper terminal setup, and checking of return values is shown below.
A corrected version of #e.jahandar's suggestion and comments from #LPs are also incorporated.
...
sid = open(dev, O_RDWR | O_NOCTTY);
if (sid < 0) {
printf("Err 1\n"); //Serial port not open
return (-1);
}
if (tcgetattr(sid, &serial_struct) < 0) {
printf("Err 2\n");
return (-2);
}
cfsetospeed(&serial_struct, (speed_t)Baudrate);
cfsetispeed(&serial_struct, (speed_t)Baudrate);
cfmakeraw(&serial_struct);
serial_struct.c_cc[VMIN] = 1;
serial_struct.c_cc[VTIME] = 10;
serial_struct.c_cflag &= ~CSTOPB;
serial_struct.c_cflag &= ~CRTSCTS; /* no HW flow control? */
serial_struct.c_cflag |= CLOCAL | CREAD;
if (tcsetattr(sid, TCSANOW, &serial_struct) < 0) {
printf("Err 3\n"); //Unable to set serial port parameters
return (-3);
}
...
#define BUFSIZE 2233
char buf[BUFSIZE + 1];
...
size_t frv;
ssize_t wrv;
...
do {
frv = fread(buf, 1, BUFSIZE, fd);
buf[frv] = 0; /* terminate string for printf */
if (frv > 0) {
wrv = write(sid1, buf, frv);
if (wrv < frv) {
/* handle error or short write */
}
} else
break;
} while (1);
...

Redundancy when reading USB serial port (C;Mac OSX;Arduino)

I'm writing a simple C program that can read data from a USB port that is connected to my Arduino device. The Arduino outputs data at a baud rate of 9600 in chunks of 4 bytes.
I want the input from the Arduino to my computer to look something like this:
136.134.132.130.129.127.126.124.121.119.117.115.113.111.
However, I'm getting something like this:
271.274.281..2.4062.4022.40225.4021
Question: How do I get the input in my C program to neatly synchronize with out loosing data/ rereading data? Are there some kind of flags that could tell my program when the port has new data?
Code:
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <sys/types.h>
int open_port(void)
{
int fd; /* File descriptor for the port */
fd = open("/dev/tty.usbmodemfd121", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
perror("open_port: Unable to open /dev/tty");
}
else
fcntl(fd, F_SETFL, 0);
struct termios options;
tcgetattr(fd,&options);
cfsetospeed(&options,B9600);
options.c_cflag |=(CLOCAL | CREAD);
tcsetattr(fd, TCSANOW, &options);
return (fd);
}
int main (){
int i;
for(i=0; i<50; i++){
fcntl(open_port(), F_SETFL, FNDELAY);
char buf[5];
size_t nbytes;
ssize_t bytes_read;
nbytes = sizeof(buf);
bytes_read = read(open_port(), buf, nbytes);
printf("%s ", buf);
buf[0]=0;
}
return 0;
}
Your program does not properly open() the serial port for reading it.
In fact it repeatedly opens it two times every iteration of the for loop.
The device should be opened only once by your program.
Instead of
for (i=0; i<50; i++) {
fcntl(open_port(), F_SETFL, FNDELAY);
bytes_read = read(open_port(), buf, nbytes);
}
the main program should be structured like
fd = open_port();
if (fd < 0) {
/* handle error condition */
}
rc = fcntl(fd, F_SETFL, FNDELAY);
if (rc < 0) {
/* handle error condition */
}
for (i=0; i<50; i++) {
bytes_read = read(fd, buf, nbytes);
if (bytes_read < 0) {
/* handle error condition */
}
}
close(fd);
Your program is too "simple". It sets only a few attributes, and doesn't bother to check the return codes of system calls.
Is this supposed to be canonical or non-canonical (aka raw) mode (i.e. is the data ASCII text or binary)?
Refer to this Serial Programming Guide for proper setup of the serial port.
read data from a USB port
USB is a bus.
The device your program reads from is a serial port attached to that USBus.
Second coding issue
Your original code may print garbage data.
nbytes = sizeof(buf);
bytes_read = read(open_port(), buf, nbytes);
printf("%s ", buf);
buf[0]=0;
The bytes returned by the read() operation are not likely to be terminated by a NULL byte, so a string operation on that read buffer could exceed the bounds of the allocated array.
Code that would not misbehave would be something like:
nbytes = sizeof(buf) - 1;
bytes_read = read(fd, buf, nbytes);
if (bytes_read < 0) {
/* handle error condition */
} else {
buf[bytes_read] = 0; /* append terminator */
printf("%s ", buf);
}
Note that nbytes is one less than the allocated size of the buffer.
This is to ensure that there is an available byte to store the string terminator byte when the read() operation returns a "full" buffer of nbytes.
For efficiency the assignment of nbytes should be performed before entering the for loop, rather than within the loop.

Resources