Using fstream with USB Serial Port
-
Thanks for the help so far. I have written a small PC App program to try termios out. I am having two issues. First is the first time I send data to the PIC, the return response is junk. Second is the response is out of sync with the sent command, it takes sending the command a second time for the response string to be correct. Would appreciate a second pair of eyes to see what is the problem. PC app code ..
int main(int argc, char** argv)
{
ComPort MyComPort;
char TXBuf[255];
char RXBuf[255];
char c;while(1) { cout << "(U)load New Map, (N)ext Map, (Q)uit .." << endl; cin >> c; c = (char)toupper(c); if (c == 'Q') return 0; if (c == 'U') { strcpy(TXBuf, "UPLOAD NEW MAP\\n"); MyComPort.WriteComPortDataMsg(TXBuf, strlen(TXBuf)); MyComPort.ReadComPortDataMsg(RXBuf); cout << "RXBuf = " << RXBuf << endl; } else if (c == 'N') { strcpy(TXBuf, "NEXT MAP\\n"); MyComPort.WriteComPortDataMsg(TXBuf, strlen(TXBuf)); MyComPort.ReadComPortDataMsg(RXBuf); cout << "RXBuf = " << RXBuf << endl; } cout << endl; } return 0;
}
class defs ..
/***** Class Declaration ******************************************************/
class ComPort
{
public:
ComPort();
~ComPort();
void WriteComPortDataMsg(char *data, char num);
void ReadComPortDataMsg(char *data, char dlimc);
void ReadComPortDataMsg(char *data);
void SetDlimiter(char dlimc);
private:
char dlim;
struct termios config;
int hComPort;
bool Error;
};void ComPort::WriteComPortDataMsg(char *data, char num)
{
for (int cnt = 0; cnt < num; cnt++)
{
write(hComPort, &data[cnt], 1); // write char to Com Port
}
tcdrain(hComPort);
}void ComPort::ReadComPortDataMsg(char *data)
{
int cnt = 0;
char c;data\[0\] = 0x00; do { read(hComPort, &c, 1); if ((c >= 0x20) && (c <= 0x7E)) data\[cnt++\] = c; } while ((c != dlim) && (cnt < 255)); data\[cnt++\] = '\\0';
}
responses ..
(U)load New Map, (N)ext Map, (Q)uit ..
u
RXBuf = MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMI did not see any code that initialises the serial port. So it will still use the current settings as shown by the stty command line tool. You have to use
tcsetattr
to select the same basic settings as on the PIC (probably 8N1; baud rate seems to mach already), disable all kinds of flow control (software and hardware), and all additional features like echoing. Example:memset(&config, 0, sizeof(config));
config.c_cflag = CS8 | CREAD | CLOCAL; // 8N1, enable receiver, ignore ctrl lines
cfsetospeed(&config, B9600); // baud rate used by the PIC
cfsetispeed(&config, B9600);
tcsetattr(hComPort, TCSANOW, &config);With new line terminated data transfers, you may also use the canonical mode (
ICANON
). -
I did not see any code that initialises the serial port. So it will still use the current settings as shown by the stty command line tool. You have to use
tcsetattr
to select the same basic settings as on the PIC (probably 8N1; baud rate seems to mach already), disable all kinds of flow control (software and hardware), and all additional features like echoing. Example:memset(&config, 0, sizeof(config));
config.c_cflag = CS8 | CREAD | CLOCAL; // 8N1, enable receiver, ignore ctrl lines
cfsetospeed(&config, B9600); // baud rate used by the PIC
cfsetispeed(&config, B9600);
tcsetattr(hComPort, TCSANOW, &config);With new line terminated data transfers, you may also use the canonical mode (
ICANON
).Jochen Arndt wrote:
I did not see any code that initialises the serial port.
There is, I was having trouble with the forum editor, thought maybe my code was too long. The first part is here ..
/***** Class Declaration ******************************************************/
class ComPort
{
public:
ComPort();
~ComPort();
void WriteComPortDataMsg(char *data, char num);
void ReadComPortDataMsg(char *data, char dlimc);
void ReadComPortDataMsg(char *data);
void SetDlimiter(char dlimc);
private:
char dlim;
struct termios config;
int hComPort;
bool Error;
};ComPort::ComPort()
{
dlim = '\n';
char c;Error = false; hComPort = open("/dev/ttyACM0", O\_RDWR | O\_NOCTTY | O\_NDELAY); if (hComPort == -1) { cout << "failed to open port\\n" << endl; cout << "Hit to Stop .." << endl; cin >> c; c = (char)toupper(c); if (c == 'S') Error = true; } if(!isatty(hComPort)) { cout << "file descriptor is not pointing to a TTY device\\n" << endl; cout << "Hit to Stop .." << endl; cin.get(c); if ((c == 's' || c == 'S')) Error = true; } if(tcgetattr(hComPort, &config) < 0) { cout << "configuration of the serial interface not found\\n" << endl; cout << "Hit to Stop .." << endl; cin >> c; c = (char)toupper(c); if (c == 'S') Error = true; } // // Input flags - Turn off input processing // // convert break to null byte, no CR to NL translation, // no NL to CR translation, don't mark parity errors or breaks // no input parity check, don't strip high bit off, // no XON/XOFF software flow control // config.c\_iflag &= ~(IGNBRK | BRKINT | ICRNL | INLCR | PARMRK | INPCK | ISTRIP | IXON); // Output flags - Turn off output processing // // no CR to NL translation, no NL to CR-NL translation, // no NL to CR translation, no column 0 CR suppression, // no Ctrl-D suppression, no fill characters, no case mapping, // no local output processing // // config.c\_oflag &= ~(OCRNL | ONLCR | ONLRET | // ONOCR | ONOEOT| OFILL | OLCUC | OPOST); config.c\_oflag = 0; // // No line processing // // echo off, echo newline off, canonical mode off, // extended input processing off, signal ch
-
Jochen Arndt wrote:
I did not see any code that initialises the serial port.
There is, I was having trouble with the forum editor, thought maybe my code was too long. The first part is here ..
/***** Class Declaration ******************************************************/
class ComPort
{
public:
ComPort();
~ComPort();
void WriteComPortDataMsg(char *data, char num);
void ReadComPortDataMsg(char *data, char dlimc);
void ReadComPortDataMsg(char *data);
void SetDlimiter(char dlimc);
private:
char dlim;
struct termios config;
int hComPort;
bool Error;
};ComPort::ComPort()
{
dlim = '\n';
char c;Error = false; hComPort = open("/dev/ttyACM0", O\_RDWR | O\_NOCTTY | O\_NDELAY); if (hComPort == -1) { cout << "failed to open port\\n" << endl; cout << "Hit to Stop .." << endl; cin >> c; c = (char)toupper(c); if (c == 'S') Error = true; } if(!isatty(hComPort)) { cout << "file descriptor is not pointing to a TTY device\\n" << endl; cout << "Hit to Stop .." << endl; cin.get(c); if ((c == 's' || c == 'S')) Error = true; } if(tcgetattr(hComPort, &config) < 0) { cout << "configuration of the serial interface not found\\n" << endl; cout << "Hit to Stop .." << endl; cin >> c; c = (char)toupper(c); if (c == 'S') Error = true; } // // Input flags - Turn off input processing // // convert break to null byte, no CR to NL translation, // no NL to CR translation, don't mark parity errors or breaks // no input parity check, don't strip high bit off, // no XON/XOFF software flow control // config.c\_iflag &= ~(IGNBRK | BRKINT | ICRNL | INLCR | PARMRK | INPCK | ISTRIP | IXON); // Output flags - Turn off output processing // // no CR to NL translation, no NL to CR-NL translation, // no NL to CR translation, no column 0 CR suppression, // no Ctrl-D suppression, no fill characters, no case mapping, // no local output processing // // config.c\_oflag &= ~(OCRNL | ONLCR | ONLRET | // ONOCR | ONOEOT| OFILL | OLCUC | OPOST); config.c\_oflag = 0; // // No line processing // // echo off, echo newline off, canonical mode off, // extended input processing off, signal ch
Use the code from my post. It sets all while you are modifying the existing where something might be still set / cleared.
CREAD
is probably set but what aboutCLOCAL
? -
Use the code from my post. It sets all while you are modifying the existing where something might be still set / cleared.
CREAD
is probably set but what aboutCLOCAL
?Jochen Arndt wrote:
CREAD
is probably set but what aboutCLOCAL
?I had the same code except .. memset
if(tcgetattr(hComPort, &config) < 0)
{
cout << "configuration of the serial interface not found\n" << endl;
cout << "Hit to Stop .." << endl;
cin >> c;
c = (char)toupper(c);
if (c == 'S') Error = true;
}
memset(&config, 0, sizeof(config));this is baudrate code ..
config.c_cflag = CS8 | CREAD | CLOCAL; // 8N1, enable receiver, ignore ctrl lines
if(cfsetispeed(&config, B9600) < 0 || cfsetospeed(&config, B9600) < 0) { cout << "Baud Rate did not set correctly\\n" << endl; cout << "Hit to Stop .." << endl; cin >> c; c = (char)toupper(c); if (c == 'S') Error = true; }
I get the same result as before. OdlG
-
Jochen Arndt wrote:
CREAD
is probably set but what aboutCLOCAL
?I had the same code except .. memset
if(tcgetattr(hComPort, &config) < 0)
{
cout << "configuration of the serial interface not found\n" << endl;
cout << "Hit to Stop .." << endl;
cin >> c;
c = (char)toupper(c);
if (c == 'S') Error = true;
}
memset(&config, 0, sizeof(config));this is baudrate code ..
config.c_cflag = CS8 | CREAD | CLOCAL; // 8N1, enable receiver, ignore ctrl lines
if(cfsetispeed(&config, B9600) < 0 || cfsetospeed(&config, B9600) < 0) { cout << "Baud Rate did not set correctly\\n" << endl; cout << "Hit to Stop .." << endl; cin >> c; c = (char)toupper(c); if (c == 'S') Error = true; }
I get the same result as before. OdlG
The source is probably here:
read(hComPort, &c, 1);
if ((c >= 0x20) && (c <= 0x7E)) data[cnt++] = c;See read(2): read from file descriptor - Linux man page[^]:
Quote:
Return Value On success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number. It is not an error if this number is smaller than the number of bytes requested; this may happen for example because fewer bytes are actually available right now (maybe because we were close to end-of-file, or because we are reading from a pipe, or from a terminal), or because read() was interrupted by a signal. On error, -1 is returned, and errno is set appropriately. In this case it is left unspecified whether the file position (if any) changes.
read()
will return zero when there are no data available. So you have either to clear yourc
variable before, or - better - check the return value:int received = read(hComPort, &c, 1);
if (received == 1 && c >= 0x20 && c <= 0x7E)
data[cnt++] = c;
else if (received < 0)
// handle error hereAnother option is setting blocking mode (attributes
c_cc[VMIN]
to non zero andc_cc[VTIME]
to the timeout value). Then you will not get zero return values but still have to check for negative return values indicating timeout or other errors. -
The source is probably here:
read(hComPort, &c, 1);
if ((c >= 0x20) && (c <= 0x7E)) data[cnt++] = c;See read(2): read from file descriptor - Linux man page[^]:
Quote:
Return Value On success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number. It is not an error if this number is smaller than the number of bytes requested; this may happen for example because fewer bytes are actually available right now (maybe because we were close to end-of-file, or because we are reading from a pipe, or from a terminal), or because read() was interrupted by a signal. On error, -1 is returned, and errno is set appropriately. In this case it is left unspecified whether the file position (if any) changes.
read()
will return zero when there are no data available. So you have either to clear yourc
variable before, or - better - check the return value:int received = read(hComPort, &c, 1);
if (received == 1 && c >= 0x20 && c <= 0x7E)
data[cnt++] = c;
else if (received < 0)
// handle error hereAnother option is setting blocking mode (attributes
c_cc[VMIN]
to non zero andc_cc[VTIME]
to the timeout value). Then you will not get zero return values but still have to check for negative return values indicating timeout or other errors.I was beginning to think I needed some way to wait on the USB/Com Port, but wasn't sure how to do that. This works great. My code change ..
void ComPort::ReadComPortDataMsg(char *data)
{
int cnt = 0;
int received;
char c;data\[0\] = 0x00; do { received = read(hComPort, &c, 1); if ((received == 1) && (c >= 0x20) && (c <= 0x7E)) { data\[cnt++\] = c; } else if (received < 0) { } } while ((c != dlim) && (cnt < 255)); data\[cnt++\] = '\\0';
}
Not sure how to deal with received < 0. Do you know where can I read up on
struct termios
and
global errno variable
? Thanks!!! OdlG
-
I was beginning to think I needed some way to wait on the USB/Com Port, but wasn't sure how to do that. This works great. My code change ..
void ComPort::ReadComPortDataMsg(char *data)
{
int cnt = 0;
int received;
char c;data\[0\] = 0x00; do { received = read(hComPort, &c, 1); if ((received == 1) && (c >= 0x20) && (c <= 0x7E)) { data\[cnt++\] = c; } else if (received < 0) { } } while ((c != dlim) && (cnt < 255)); data\[cnt++\] = '\\0';
}
Not sure how to deal with received < 0. Do you know where can I read up on
struct termios
and
global errno variable
? Thanks!!! OdlG
For all standard C library features you can get the description from the shell command prompt as man page:
man errno
will shown the content of the errno(3) - Linux manual page[^]. Similar for termios which is covered by a single man page: termios(3) - Linux man page[^]. If you want to get the above links, just use the same "man " with your favorite search engine. Other good sources for the standard C and C++ libraries are cplusplus.com - The C++ Resources Network[^] and cppreference.com[^].
-
The source is probably here:
read(hComPort, &c, 1);
if ((c >= 0x20) && (c <= 0x7E)) data[cnt++] = c;See read(2): read from file descriptor - Linux man page[^]:
Quote:
Return Value On success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number. It is not an error if this number is smaller than the number of bytes requested; this may happen for example because fewer bytes are actually available right now (maybe because we were close to end-of-file, or because we are reading from a pipe, or from a terminal), or because read() was interrupted by a signal. On error, -1 is returned, and errno is set appropriately. In this case it is left unspecified whether the file position (if any) changes.
read()
will return zero when there are no data available. So you have either to clear yourc
variable before, or - better - check the return value:int received = read(hComPort, &c, 1);
if (received == 1 && c >= 0x20 && c <= 0x7E)
data[cnt++] = c;
else if (received < 0)
// handle error hereAnother option is setting blocking mode (attributes
c_cc[VMIN]
to non zero andc_cc[VTIME]
to the timeout value). Then you will not get zero return values but still have to check for negative return values indicating timeout or other errors.Question on how to error handle. I am falling into the received < 0 but if I just ignore it and keep looking for chars, I get a complete, correct message. Is it safe to do that or should I clear the error? And How? Thanks in advance ;) OdlG
-
Question on how to error handle. I am falling into the received < 0 but if I just ignore it and keep looking for chars, I get a complete, correct message. Is it safe to do that or should I clear the error? And How? Thanks in advance ;) OdlG
What is the error (
errno
) code then? -
What is the error (
errno
) code then?Looks like it is 11
-
Looks like it is 11
That is
EGAIN
(you can look it up in errno.h and get the corresponding error message with strerror(3) - Linux manual page[^] ) and in most cases (as here) not an error. You are just calling read again too fast (the next character has not been received so far). To avoid this you can set a timeout value. Then theread()
will block until data are available or a timeout occured.