03-21-2025 12:05 PM
Hello everyone,
I am working on a project where I need to send messages at a frequency of 500Hz with a baud rate of 460800. Unfortunately, I have found that using NI VISA is not very feasible for this purpose. As a result, I have decided to create a LabVIEW application that uses Linux’s native COM port functions.
To achieve this, I am using the libc.so.6 library and have come across a C code snippet that seems to be useful for setting up the serial port and sending data. However, I am encountering issues with the struct definition. I suspect the problem lies in how I am defining the termios
structure because every time I call the tcgetattr
function, the connection with the controller gets lost.
The function is defined as follows:
extern int tcgetattr (int __fd, struct termios *__termios_p) __THROW;
I have attached the VI I’ve created, along with the header file and .so file in a zip archive.
Can someone help me figure out how to properly define the termios
structure or provide some guidance on why the connection is dropping when tcgetattr
is called?
Any assistance or suggestions would be greatly appreciated!
C Code:
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <stdio.h>
int main() {
int serial_port = open("/dev/ttyS0", O_RDWR);
if (serial_port < 0) {
perror("Unable to open port");
return 1;
}
struct termios tty;
if (tcgetattr(serial_port, &tty) != 0) {
perror("Error from tcgetattr");
return 1;
}
cfsetispeed(&tty, B460800);
cfsetospeed(&tty, B460800);
tty.c_cflag &= ~PARENB;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8;
tty.c_cflag |= CREAD | CLOCAL;
if (tcsetattr(serial_port, TCSANOW, &tty) != 0) {
perror("Error from tcsetattr");
return 1;
}
// Seri port üzerinden veri gönderme işlemi
char msg[] = "Hello, Serial Port!";
write(serial_port, msg, sizeof(msg));
close(serial_port);
return 0;
}
03-23-2025 07:15 AM - edited 03-23-2025 07:43 AM
First, would you care to specify how you determined that NI-VISA is not feasible for this? 500 Hz frequency to send of a message isn't really beyond VISAs capability, unless that sending of a command also encompasses a receiving of an acknowledgement before sending the next command. Then you end up having latencies, but they are mainly determined by how fast your device will respond and will be the same when accessing the termios API directly.
Second, you have already found that passing of structs is done by using a LabVIEW cluster and configuring the parameter as adapt to type. But that requires you to be able to play C compiler to a higher degree than if you would write the according C code. There are things like the individual type of data elements which needs to match exactly. Just assuming that everything is an int is not a good strategy. Also you have element alignment and last but not least you make the classical error of assuming that a LabVIEW array is the same as a C array. It is not! A LabVIEW array is a handle (pointer to pointer with embedded length element) while a C array is simply a pointer without any implicit length information. Except of course when the array is fixed size, which many arrays inside a structure usually are. Then it is inlined in the structure and more analogues to another LabVIEW cluster in the outer cluster, with the number of elements inside, that the fixed size array contains.
Basically you are heading into finicky low level C compiler details and to make matters worse, every platform can use different rules. Also Linux being an open source system, where the GNU imposed modus operandi is to compile everything from sources including your applications, makes it less of a guarantee that the binary ABI between various versions, flavors and incarnations of it stay the same. While the termios structure is probably not something that the GNU folks will change on a pure whim, it's also not guaranteed not to, simply because they can, so why should they not? Basically just googling the structure and hoping to be done is not exactly a safe mode, even though this is about glibc, which almost all Unixes (and some other systems) use in one way or the other. The only canonically valid source for this information is the source code that was used to compile the according system, so you would have to get the Linux RT sources from the NI github repository and search there for the actual definition of the termios (and any other structure you will eventually come across too). And strictly speaking every of the Linux RT versions might have differences in there although as mentioned, for glibc this is usually very unlikely. But assuming that it is exactly the same as any other Unixy like system that you find on Google is definitely not a safe thing.
So, yes it can be done, but should you? That will be up to you. Expect this to cost you a lot of time and some really grey hairs too, and get used to restart your controller regularly (and possibly even have to wipe it and reinstall it occasionally) when heading down that path. And last but not least, just because it doesn't crash anymore does not mean that it is not still corrupting some memory somewhere. Add this to your evaluation about if you should use this approach to implement a real-time system that quite often is supposed to run unattended for weeks, months or even years and may in the final application do serious safety related operations.