Can Only Read From Serial Port in Function That Opened It [Archive] - C Board

PDA

View Full Version : Can Only Read From Serial Port in Function That Opened It


HalNineThousand
03-21-2008, 11:37 PM
I had this working, thanks to help in another thread, and now when I open the serial port for reading and writing, I can ONLY read from the serial port in the same function where I opened it.

This is not all the code in the file, but I start with my variables:

//Seperate namespace to keep them invisible to other files
namespace {
bool isOpen = false;
int infd = 0, outfd = 0, lastread = 0;
unsigned long naptime = 100;
long BAUD = B115200;
long DATABITS = CS8;
long STOPBITS = 0;
long PARITYON = 0;
long PARITY = 0;

// Default value: use the RS232 port
string serdev = "/dev/ttyS0";
}


Then, in order of use (but almost reverse order of how they're in the file), I have a function to open the serial port as a file:

void openport() {

if (isOpen) return;
isOpen = true;
cout << "Opening port: " << serdev << endl;
infd = open(serdev.c_str(), O_RDWR | O_NOCTTY);
fcntl(infd, F_SETFL, 0);
setportattr(infd);
//Open for output later (device doesn't work well using same FD for in and out!)
// outfd = open(serdev.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
// setportattr(outfd);

if (infd == -1 || outfd == -1) {
if (infd == -1)
perror("Unable to open port for reading");
if (outfd == -1)
perror("Unable to open port for writing");
exit(1);
} else {
cout << "Port " << serdev << " has been opened. Input: " << infd << ", Output " << outfd << endl;
}
//Test read the port from in this function -- always works and reads a byte
cout << "====>Attempting to read port: " << infd << endl;
char* buffx; int rdx;
rdx = read(infd, buffx, 1);
cout << "====>FD: " << infd << ", Read length: " << rdx << endl;
//Call other routine to test read it from outside function - always fails, even
//if the test above is removed, so it's not a matter of if there's no data
fakeread();
return;
}

I initialize the serial port parameters here:

void setportattr(int portfd) {

struct termios options;

tcgetattr(portfd, &options);
//Should never change, set at 115200
options.c_cflag |= BAUD;
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_cflag |= (CLOCAL | CREAD);
options.c_iflag |= (IXON | IXOFF | IXANY);
options.c_cflag &= ~CRTSCTS;
int rc = tcsetattr(portfd,TCSANOW ,&options);
cout << "FD: " << portfd << ", Attr return code: " << rc << endl;
return;
}

Setting attributes is always successful. I've also tried reading a byte from the file in this function. It's worked in some cases.

Then there's the simple function to do another test read:

void fakeread() {
cout << "---->Attempting to read port: " << infd << endl;
char* buffy; int rdy;
rdy = read(infd, buffy, 1);
cout << "---->FD: " << infd << ", Read length: " << rdy << endl;
return;
}


If I put read() statements anywhere in the openport() function, it works, but once I move it out and into another function, it ALWAYS fails (but it has worked if it was in the setportattr() function, at the very end of that function).

This was working perfectly this afternoon. I can't figure out what I did that might have changed it that much, but now I can only read from the device from within that function. The debugging output I've printed (statements are included in above samples) shows that it's using the right file descriptor.

Is this something so bleeding obvious that I'm not seeing it?

Thanks for any help on this!

matsp
03-22-2008, 05:17 AM
In your read function, char* buffy is not initialized, so it's highly unlikely to point to memory that can be written to. Perhaps using a char buffy, and read(... &buffy ...)

--
Mats

HalNineThousand
03-22-2008, 08:39 AM
That seems to make a difference. Believe it or not, what I had was originally copied from a tutorial on the web.

It had worked like that previously in another function. Any idea why it was working before and stopped at some point?

This leads to another question. When it was declared as char* buff (or char* buffx or buffy of buffz -- used different names in each function attempt), nothing was declaring how long buff was. Is that part of the problem? And is it possible to dynamically declare the length of char buff[] when the function is called depending on the number of characters requested to be read in?

matsp
03-22-2008, 08:57 AM
That seems to make a difference. Believe it or not, what I had was originally copied from a tutorial on the web.

It had worked like that previously in another function. Any idea why it was working before and stopped at some point?


Because "undefined behaviour" (which writing to the address contained in an uninitialized variable is definitely an example of) is like running through a road-junction when the lights are red - you may get away with it sometimes, but at other times the police catches you, or a driver comming the other way hits your car. It's NEVER a good idea to do undefined behaviour, and writing to undefined pointers isn't one that is likely to work very often - even if it works to write to the location your pointer HAPPENS to have by coincidence, it's unlikely that the location is a good one for your data.


This leads to another question. When it was declared as char* buff (or char* buffx or buffy of buffz -- used different names in each function attempt), nothing was declaring how long buff was. Is that part of the problem? And is it possible to dynamically declare the length of char buff[] when the function is called depending on the number of characters requested to be read in?

In standard C, variable length arrays is not allowed - you have to use malloc to allocate your own memory [or use some estimation of how much data you want to receive at a time and use a fixed size array]. Just make sure whatever method you use, never give "read()" a bigger "size" than your buffer.

Since serial ports are relatively slow, using a small fixed size buffer and then assembling the "entire packet" as you go along in some way is perfectly fine [e.g. allocate a bugger based on the packet content, dynamically grow a packet container as it fills up, or whatever method you prefer].

--
Mats

laserlight
03-22-2008, 09:04 AM
In standard C, variable length arrays is not allowed
From what I understand, variable length arrays are a feature of C99, so they are allowed in standard C.

HalNineThousand
03-22-2008, 09:08 AM
Thanks! I didn't realize that not specifying a length for buff in the first case was leaving it as undefined -- especially since I had seen that in a tutorial! I know what you say about undefined behavior. I used Assembler back in the '80s on an Apple //e and lately I've been using Java, so I'm getting used to C++ being in-between where I can use a variable that isn't defined but should not.

In this case, and I could change it, I'm only reading one character at a time because I have to evaluate each character to see if it starts a command string, give the length of that string, or is the checksum at the end of the string. (And, of course, I have to add the value of each byte in between to compute the checksum and I can either do that when it comes in or go through a 2nd loop with all the same objects.)

matsp
03-22-2008, 09:35 AM
Laserlight is of course right. C99, which is the "new" standard allows variables to be used as the size of arrays. As does C++ of the current ratified standard. However, according to the standard the value still needs to be a constant value. So you can have something like this:

int func(const int size)
{
char array[size];
...
}

but this is non-standard [some compilers do support it tho']:

int func()
{
int size;
...
// do something to set size, e.g. read some user-input or such
char array[size];
...
}


--
Mats