I'm trying to use fgets
in esp-idf to read a whole line from UART.
The basic reference code is the select example. So far, here my code:
#define DLP_RFID2_BUF_SIZE 256
static char buffer[DLP_RFID2_BUF_SIZE];
uart_config_t uart_config =
{
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1, DLP_RFID2_BUF_SIZE * 2, 0, 0, NULL, 0));
ESP_ERROR_CHECK(uart_param_config(UART_NUM_1, &uart_config));
ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, PIN_DLP_RFID2_TX, PIN_DLP_RFID2_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
if ((fd = open("/dev/uart/1", O_RDWR)) == -1)
{
ESP_LOGE(TAG, "Cannot open UART");
return fd;
}
uart_vfs_dev_use_driver(UART_NUM_1);
here the loop function:
int s;
fd_set rfds;
struct timeval tv = {
.tv_sec = 0,
.tv_usec = 20000,
};
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
s = select(fd + 1, &rfds, NULL, NULL, &tv);
if (s < 0)
{
ESP_LOGE(TAG, "Select failed: errno %d (%s)", errno, strerror(errno));
}
else if (s == 0)
{
ESP_LOGI(TAG, "Timeout has been reached and nothing has been received");
}
else
{
if (FD_ISSET(fd, &rfds))
{
if (fgets(buffer, sizeof(buffer), ???))
{
// do something
}
}
else
{
ESP_LOGE(TAG, "No FD has been set in select()");
}
}
the fgets function requires a FILE *
variable as third parameter.
But I only have int
s (fd
, s
) and fd_set
(rfds
).
I tried to change the code in order to use FILE *
:
FILE *f = fopen("/dev/uart/1", "rw");
// ...
if (fgets(buffer, sizeof(buffer) - 1, f))
{
// do something
}
since fgets
is blocking I need to check if there are some data first.
But now select
requires an fd_set
and not a FILE *
:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
How can I "convert" FILE *
to fd_set
or int
to FILE *
?
Your problem is that you need to use a file descriptor for select
(2), but need a FILE
for fgets
(3).
There are two possibilities here.
Given a FILE
object, you may be able to use the function fileno
to get the corresponding file descriptor for use in select
.
Alternatively, if you initially open the file using open
(2), to get a file descriptor, you may be able to use fdopen
(3) to create a FILE
object from it.
I say may in both cases, because neither fileno
nor fdopen
is defined in the C standard, so if you're in a restricted environment (which your mention of UART hints at) it's possible they're not available. They're both very widely implemented, though, so if your library is big enough to have fgets
, it'll surely have at least one of the two.
Note that there's no guarantee that these functions will work. The C standard (2018, Sect.7.21) says that the FILE
struct...
is an object type capable of recording all the information needed to control a stream, including its file position indicator, a pointer to its associated buffer (if any), an error indicator that records whether a read/write error has occurred, and an end-of-file indicator that records whether the end of the file has been reached
Thus it doesn't say anything about file descriptors, and specifically doesn't require that the struct contains any of the information that would enable either fileno
or fdopen
to work. So on paper, all bets are off here, and it would be quite legitimate for a C library to implement FILE
in such a way. But that would be unusual in most ‘normal’ C contexts.