Once more, the idea is very simple: I am sending a command to a device, and the device interupt me and replays. I read what was send, I work with the replay data, and according to that data, I am sending another command.
You are describing a common and simple arrangement of master & slave devices: the master device sends a command or request message, and the slave device has to reply with a response message.
You are using the proper POSIX method of modifying the flags. However the system calls fcntl()
, tcgetattr()
and tcsetattr()
should have their return codes checked to ensure there were no errors.
You are configuring the serial port for non-canonical (aka raw) input & output. (So your receiving buffer should be unsigned char
rather than signed char
.) This mode offers a method of reading based on byte count and/or silence on the serial link. There is no reason to use async read involving a signal handler. (Your signal handler has the same bug as this question.) The master/slave device relationship dictates a request/response message exchange.
To properly read the complete response message, you need to define the VMIN and VTIME values. If the response message is 7 to 32 bytes in length, then
termAttr.c_cc[VMIN] = 32;
termAttr.c_cc[VTIME] = 5;
should do the trick. You would be expecting a response message of no more than 32 bytes, or when bytes stop arriving after a half second, then assume the message is complete.
#define CMDLEN 7
unsigned char buffer[32];
unsigned char cmd[CMDLEN];
while (connected == 1) {
/* construct a new command */
/* send request */
nbytes = write(fd, cmd, CMDLEN);
if (nbytes != CMDLEN) {
/* problem! */
}
/* get response, wait if necessary */
nbytes = read(fd, buffer, sizeof(buffer));
if (nbytes < 7) {
/* problem! */
}
/* process response of nbytes */
}
Addendum: response to questions in comments
First, can I leave the filling of the buffer in the signal handler with defined values for VMIN and VTIME?
You have not described any requirement for asynchronous reading of the serial port. (BTW this is not a "serial bus"; USB is a serial bus; EIA/RS-232 is a serial link.) If this is not a master/slave configuration, then please say so. Otherwise a signal handler for reading is an unnecessary complication, and unnecessary complexity is (usually) an indication of poor design. (Exception would be if this code is part of a system that is exclusively event driven.)
Be aware that the serial port is fully buffered for both reading and writing. Your program does not have to have a pending or active read()
request when data arrives at the serial port. The kernel stores the serial data in an internal buffer as the data is received. Your program will not lose or miss any data even if it does not use async reading and a signal handler.
The last question is about fcntl(), tcgetattr() and tcsetattr() sistem call. Can you please give me some examples about what error flag I should check?
Such information is documented in the Linux man pages.
If you are using a Linux development system (and you should), then make sure that the man pages are installed. Otherwise you can google for the man page, e.g. "fcntl linux man page"
Check the function prototype.
The returned value is typically the syscall status. If an error is indicated (often by a negative value such as -1), then the global variable errno should be accessed to get more detail.
rc = tcgetattr(fd, &termAttr);
if (rc < 0) {
printf("failed to get attr: %d, %s", rc, strerror(errno));
close(fd);
exit (-2);
}
P.S I am sending an example of the message that I am sending to the device, and an response, so that you can see for what I am talking about.
tx_buffer = 0xFC 0x05 0x11 0x27 0x56 rx_buffer = 0xFC 0x05 0x40 0x27 0x56 0xFC always same 0x05 number of bytes 0x11 command 0x40 response code 0x27 crc_low 0x56 crc_high
example of one command packet and response packet
That is a typical message format for a binary protocol.
As part of the read processing, each received message must be validated before any message processing is performed.
The 0xFC
header byte must be checked to ensure that you have message frame synchronization. If the first byte is not equal to 0xFC
, then the program has to go into a "hunt for synchonization" mode to find the start of a message (starting with the second byte in this buffer).
The message length should be checked for reasonableness and then used to locate the two CRC bytes, and the message contents can be validated. If the CRC check fails, then the "message" should be ignored and a "hunt for sync" should commence (starting with the second or third byte of this "message").
Addendum 2: response to question on "final code
So the question is now how to create a timer?
Userland can schedule a periodic timer using a POSIX timer and a signal, i.e. timer_create()
.
But you'll have to check the available clock resolutions to determine if it will let you do 1ms.
But IMO you are going down the wrong path.
You have ignored my advice on not using async I/O.
You are calling read()
in a signal handler, which is a potential problem if it tries to sleep.
Your signal handler for async reading has no synchronization or buffer management with the main thread, so the program is at risk of losing data that was read.
You have ignored my advice on checking return codes of syscalls (except for write()s
).
If I was writing this program, I would probably use a state machine and there would be only one write()
and a read()
using the serial port.
Addendum 3: response to 3rd set of questions in comments
Can you please give me a simple example of how can I create a timer, with the timer_create() function?
Apparently you didn't heed my previous advice on using man pages.
There's a code example in the timer_create() man page
About this: “Your signal handler for async reading has no synchronization or buffer management with the main thread, so the program is at risk of losing data that was read.” I think I can solve this If I call clearRXbuffer function after every read() function in the signal handler, so in this case the buffer will only contain the last message data which can be from 7 to 32 bytes. So if new message arrives it will be written in the beginning of the buffer. What do you thing, is this idea good, and am I going in right direction?If not, can you please give me some other buffer management idea.
That is not a reliable solution.
The signal handler executes asynchronously with respect to the main thread.
All shared variables and buffers are therefore critical regions.
Critical regions have to be protected with synchronizing constructs such as mutual exclusion locks (aka mutexes) or semaphores or conditional variables.
Leaving a critical region (like the receive buffer) unprotected might seem to work okay some of the time, but will be unreliable and cause (undetected) loss of data or "strange" program behavior.
You can try various "band-aids" like clearing the buffer, but in the end only proper synchronizing constructs (provided by the OS that execute atomically) will work reliably.
The system maintains a FIFO buffer for the data as it arrives from the serial port, so that no data is lost. Your signal handler counteracts that FIFO by using a single destination buffer that overwrites its buffer everytime the signal handler is executed. The only way this program won't lose any data is to somehow ensure that every write()
, read()
and CheckRXbuffer()
are perfectly coordinated, and the remote device behaves perfectly and responds in a timely manner all the time.
But those are not conditions that a program can depend on when you have asynchronous reads and no synchronizing mechanisms in place!
Tutorials on multithreading usually cover these concepts.
Can I use one write()function, and then another one write() function, because I tried to do that, but it only is written once. Do you know why is that happening?
No.
... am I going in right direction?
Strange that you bother to ask for my opinion on a minor item, when you choose to completely ignore my advice on the major topic of (mis)using async I/O.
Addendum 4: response to 4th set of questions in comments
I want to tell you that in the datasheet of the device is mentioned that I need to use async I/O because ...
Wow, after 2 requests by me (original response & 1st addendum), you finally mention 2 days later why you think you need to use async I/O.
because when I try to power up the device, or reset the device, I should sent N number of commands, and after N number of commands the device response to me.(power up device sequence).
That does not require "async I/O".
Something like this also can happen when I am sending a status to the device, and the device for some reason won’t response to me, in amount period of time, so I should resend the command (async I/O).
That does not require "async I/O" either, nor does that describe async I/O.
because it’s written in the datasheet
Is this datasheet available in English online?
Are you confusing "asynchronous communication" with "asynchronous I/O"?
If this device only sends data in response to a command, and never sends data spontaneously on its own accord (i.e. client/server model), then that means that your program can expect or knows when it can expect messages from the device.
These would be messages that were "solicited" or requested from the device.
The device (from what you have described so far) never sends unsolicited messages.
If this device did send unsolicited messages, then yes, your program might need to have async I/O capability to process these message in a timely manner.
But again, the device (from what you have described so far) only sends solicited messages or no messages when it receives a command.
A device should not be dictating how the host program should be structured. It can only specify the protocol.