简体   繁体   中英

How do i set up termios parameters in C++ for a Raspberry PI USB UART connection?

I have a RPI 3 and a LoRa USB module and I'm writing some code in C++ for the port connection to the device. I am able to connect to the port (which is assigned as ttyUSBPort1 in a udev rule). However when I am sending data to the port, I'm getting an error. I just don't know enough about termios and port communications to determine if that's the problem (yes, I've read the manpage).

The LoRa module is a RN2903 device and the following is the UART Interface instructions on the reference sheet:

All of the RN2903 module's settings and commands are transmitted over UART using the ASCII interface. All commands need to be terminated with < CR >< LF > (spaces added for formatting) and any replies they generate will also be terminated by the same sequence. The default settings for the UART interface are 57600 bps, 8 bits, no parity, 1 Stop bit, no flow control.

When sending commands, I can see that the device is responding with "invalid_parameter" by monitoring the port with

sudo cat /dev/ttyUSBPort1

I am assuming either I have some of the termios flags set incorrectly, or the write command set up incorrectly. Here's the code I have that sets up the port:

int openPort(void) {
struct termios tty;
memset(&tty, 0, sizeof tty);
if ((usb_port = open(device, O_RDWR))>=0) {// | O_NOCTTY | O_SYNC
    std::cout << "DEVICE OPENED: " << device << " handle number: " << usb_port << std::endl;
} else {
    fprintf(stderr, "unable to open serial device");
    return -1;
}
if(tcgetattr(usb_port, &tty) != 0) {
    printf("Error %i \n", errno);
}
cfsetispeed(&tty, B57600);
cfsetospeed(&tty, B57600);

tty.c_cflag     &=  ~PARENB;            // Make 8n1
tty.c_cflag     &=  ~CSTOPB;
tty.c_cflag     &=  ~CSIZE;
tty.c_cflag     |=  CS8;

tty.c_cflag     &=  ~CRTSCTS;           // no flow control
tty.c_cc[VMIN]   =  0;                  // read doesn't block
tty.c_cc[VTIME]  =  5;                  // 0.5 seconds read timeout

tcflush( usb_port, TCIFLUSH );
if (tcsetattr(usb_port, TCSANOW, &tty) != 0) {
    printf("Error %i\n", errno);
}
return usb_port;
}

And here's the call command to get the version information from the device:

void radioCMD(string tmp) {
string tmp2 = tmp + "\r\n";
int n = tmp2.length();
char cmd[n];
strcpy(cmd, tmp2.c_str());
std::cout << write(usb_port, cmd, sizeof(cmd)) << " " << cmd << "Writing to " << usb_port << " Delay: " << delay << " Command Size: " << sizeof(cmd) << std::endl;
}
void setupRadio() {
radioCMD("sys get ver");
usleep(delay);
}

When writing to the console std::cout, I am seeing this:

13 sys get ver
Writing to 3 Delay: 200000 Command Size: 13

showing that the message is indeed being correctly written.

The cat output from the device should respond with something like this (from the datasheet):

2.3.6.1 sys get ver Response: RN2903 XYZ MMM DD YYYY HH:MM:SS, where XYZ is the firmware version, MMM is month, DD is day, HH:MM:SS is hour, minutes, seconds (format: [HW] [FW] [Date] [Time]). [Date] and [Time] refer to the release of the firmware. This command returns the information related to the hardware platform, firmware version, release date and time-stamp on firmware creation. Example: sys get ver

What I actually get is "invalid_param\\r\\n", which is the appropriate response from the device if something in the call is not correct.

Any ideas where I might be going wrong here?

EDIT

thanks to Ted for pointing me in the right direction and simplifying my code. There were two missing termios flags. Once I set these (last two), it works fine.

tty.c_cflag     &=  ~PARENB;            // Make 8n1
tty.c_cflag     &=  ~CSTOPB;
tty.c_cflag     &=  ~CSIZE;
tty.c_cflag     |=  CS8;
tty.c_cflag     &=  ~CRTSCTS;           // no flow control
tty.c_cc[VMIN]   =  0;                  // read doesn't block
tty.c_cc[VTIME]  =  5;                  // 0.5 seconds read timeout
        ***ADDITIONAL TWO FLAGS THAT FIXED IT****
tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed
tty.c_oflag &= ~OCRNL; // Prevent conversion of newline to carriage return/line feed

New Write Call Function:

void radioCMD(string cmd) {
    cmd += "\r\n";
    write(usb_port, cmd.c_str(), cmd.size());
}

This creates a VLA that is one char too short to fit the null terminator of the C string:

void radioCMD(string tmp) {
    string tmp2 = tmp + "\r\n";
    int n = tmp2.length();
    char cmd[n];                       // should be n+1 for strcpy to work
    strcpy(cmd, tmp2.c_str());         // undefined behavior \0 is written out of bounds
    write(usb_port, cmd, sizeof(cmd)); // sizeof(cmd) should be replaced by n
}

A better alternative would be to use std::memcpy instead of std::strcpy to copy the C string without the null terminator - and to avoid VLA :s.

An even better alternative would be to use the std::string you get as a parameter directly:

void radioCMD(string cmd) {
    cmd += "\r\n";
    write(usb_port, cmd.c_str(), cmd.size());
}

It may not be the only problem, but as the std::strcpy currently makes your program have undefined behavior, it's a good place to start.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM