I had a few problems before with writing to the serial port in Linux and everything worked for while, but now, after everything else is done and I'm checking over all the functions, I'm running into trouble again. I'm having 2 problems and since they involve most of the same code, they might be intermixed.
I'm using this code as part of a program to control with a radio through the serial port. The radio is activated when the DTR line goes high and I can communicate with it to read and write data and when the DTR drops, the radio turns off. There are also on and off commands, but they seem to basically turn the output on and off. In other words, the DTR seems to be the master on/off and the commands for on/off seem to be more for a sleep mode.
The first problem is with the DTR line. It raises when I open the port for reading, which it should do, but it is not dropping when I close the port. In other experiments (including one I mentioned before with Perl), when I killed the program, the DTR line dropped. That was happening before and isn't now. I even reverted to the earlier code I used to open the port and the DTR still doesn't drop when I exit the program. I can go into details if needed, but in short, it's important that the DTR drop when this program exits, crashes, or is killed. I know I can code it in, but I have no guarantee that code will be executed in case of a core dump or in other situations.
The second problem is that I think I have the settings so when I try to read a byte, it'll block until one is available. That's not happening.
I open the port twice. This has been working well for me. I'm reading with one descriptor and writing with the 2nd. I tried opening it only once, but it never seemed to work that way. (That was a few weeks ago, so I don't remember exactly why it didn't work.) When I close it at program exit, I close using both file descriptors.
Here's the code to open the port:
Code:
bool LinuxPort::openport() { //public
//isOpen, infd, outfd are global and of the appropriate type for what they do
if (isOpen) return true;
isOpen = true;
cout << "Opening port: " << serialdevice << endl;
infd = open(serialdevice.c_str(), O_RDWR | O_NOCTTY);
fcntl(infd, F_SETFL, 0);
//code included for setportattr below
setportattr(infd);
outfd = open(serialdevice.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
//Yeah, we just set it for input, but for some reason there are consistently better
//results if we do it here, too.
setportattr(outfd);
if (infd == -1 || outfd == -1) {
if (infd == -1)
perror("\tUnable to open port for reading");
if (outfd == -1)
perror("\tUnable to open port for writing");
// exit(1);
return false;
} else {
cout << "\tPort " << serialdevice << " has been opened. Input: " << infd << ", Output " << outfd << endl;
}
return true;
}
Here is the code I use to set the port attributes. I don't set the DTR because it goes high automatically:
Code:
void LinuxPort::setportattr(int portfd) { //public
//BAUD is B115200
int status;
//Should never change, set at 115200
tcgetattr(portfd, &options);
options.c_cflag = 0 | BAUD;
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~CRTSCTS;
options.c_iflag = 0 | (IGNBRK | IGNPAR);
options.c_iflag = (IXON | IXOFF | IXANY);
options.c_oflag = 0;
options.c_lflag = 0;
int rc = tcsetattr(portfd, TCSANOW, &options);
cout << "Attribute set return code: " << rc << endl;
return;
}
I've experimented with settings. For now this is on /dev/ttyS0, but it needs to also work through an adapter at /dev/ttyUSB0. These settings have worked before and using them I did have it working so when I killed the program or exited it, the DTR always dropped.
And here's the code to read a byte from the port. I'm including both problems because I don't know if they spring from the same problems with opening the port.
Code:
//for now ilen is always 1 byte
char* LinuxPort::hdreadbytes(int ilen) { //public
char* buff = new char[1024];
unsigned char cIn;
int i, rd = 0;
bool loopflag = false;
buff[0] = 0;
if (ilen > 1024)
return buff;
if (!isOpen) {
cout << "Having to re-open port\n";
openport();
}
while (rd < 1) {
//In debugging this line prints over and over, showing it loops and doesn't block.
// cout << "Waiting for byte...\n";
rd = read(infd, buff, ilen);
usleep(naptime);
loopflag = true;
}
lastread = rd;
return buff;
}
I don't think the close code is relevant, since my concern is that the DTR doesn't drop when the program is killed, but even when the close code is used, the DTR does not drop.
Code:
void LinuxPort::closeport() { //public
//infd & outfd are file descriptors
int portfd, status;
isOpen = false;
if (outfd == 0 && infd == 0)
return;
if (verbose) printdtr("About to close ports");
if (outfd > 0) {
close(outfd);
}
if (infd > 0) {
close(infd);
}
return;
}
I am still new to C++ and I'm still learning about the serial port. At this point, this looks like it should work to me. I've done what one tutorial says that should make the port block while waiting to read a new byte, but it's not working. I'm not sure what I'm doing that holds the DTR line high when I quit. With other programs I've tried that use the serial port, if the program is killed the DTR drops automatically.
I'm also not clear why I need to open the port to read from it and again to write to it. I think the problem was I wanted it to block on reads but not on writes.
Since I have the usleep() statement in the read loop, I can get by without fixing that (although I'd like to, if for no other reason to understand what I'm doing wrong), but I do need to find out why the DTR is not dropping.
Thanks for any help on this!