This has been a rather frustrating problem. Basically, I'm writing a sequence of bytes to an external device connected via serial, and to debug the program, I'm echoing it back to my Linux machine (Ubuntu 14.04 LTS).
For the most part, most of the functionality seems to work. For instance, I write 16 bytes, and I receive 16 bytes. However, the received 16 bytes appear to be remapped:
Serial Write: AF 0A D1 07 01 00 10 00 00 00 00 00 00 00 00 00
Number of bytes written: 16
Serial Response: AF CB DD F7 FD FF 9F FF FF FF FF FF FF FF FF FF
Bytes Received: 16
This is a consistent pattern: AF -> AF
, 00 -> FF
, D1 -> DD
, and so forth.
The code that is processing this is fairly simple (there are three parts).
The first part reads from a pipe that contains the data to be written, and then sends it to the specified tty
file:
void* ttyPortWrite(void*)
{
//Local Variables
int err, i, j, bytesRead, bytesWritten = 0;
uint8_t bufferBytes[6144];
std::string cadetWrite = "";
uint8_t ch[1];
pollfd pollList;
//Create a pipe
err = pipe(fileDesc);
if(err < 0)
{
pthread_mutex_lock(&stdIO);
std::cerr << "\nThere was an error creating a pipe.\nThe program will now exit."
<< std::endl;
pthread_mutex_unlock(&stdIO);
exit(-1);
}
//Build poll structure
pollList.fd = fileDesc[0];
pollList.events = POLLIN | POLLPRI;
//Continuously write out to terminal
while(1)
{
//Flush streams
pthread_mutex_lock(&stdIO);
std::cout << std::endl;
pthread_mutex_unlock(&stdIO);
//Poll pipe
if(poll(&pollList, (unsigned long) 1, -1))
{
//Read one byte at a time, looking for special character (~)
for(i = 0;; i++)
{
bytesRead = read(fileDesc[0], &ch, 1);
if(bytesRead < 0)
{
pthread_mutex_lock(&stdIO);
std::cerr << "\nREAD ERROR: System reported bytes were ready to be read, but read() returned an error code: "
<< "\n\t"
<< strerror(errno)
<< std::endl;
pthread_mutex_unlock(&stdIO);
}
else if(bytesRead == 0)
{
pthread_mutex_lock(&stdIO);
std::cout << "\nPOTENTIAL ERROR: System reported bytes were ready to be read, but no bytes were found."
<< std::endl;
pthread_mutex_unlock(&stdIO);
}
else
{
//Quick check if special character was found
if(ch[0] == 0x7E)
{
//Print out to user
pthread_mutex_lock(&stdIO);
printf("\nSerial Write: ");
for(j = 0; j < i; j++)
printf("%02X ", bufferBytes[j]);
pthread_mutex_unlock(&stdIO);
//Write to TTY file
bytesWritten = write(fdSerial, &bufferBytes, i);
if(bytesWritten <= 0)
{
pthread_mutex_lock(&stdIO);
std::cerr << "\nTTY WRITE ERROR: Bytes were ready to be written to TTY file, but the system reported an error during a write attempt."
<< std::endl;
pthread_mutex_unlock(&stdIO);
}
else
{
pthread_mutex_lock(&stdIO);
std::cout << std::endl
<< "Number of bytes written: "
<< bytesWritten;
pthread_mutex_unlock(&stdIO);
}
//Break out of loop
break;
}
else
{
//Save to buffer
bufferBytes[i] = ch[0];
}
}
}//End for
} //End if poll()
} //End while
}//End function
I'm fairly certain there isn't anything wrong with this function. The input written to the pipe (which is handled by another thread), is read byte for byte by this thread, and it matches perfectly.
The second snippet of code simply waits for data to come in to the tty file:
void* ttyPortRead(void*)
{
//Local Variables
uint8_t bufferBytes[2048];
int i = 0;
int bytesRead = 0;
std::string cadetResponse = "";
pollfd pollList;
//Build poll structure
pollList.fd = fdCadet;
pollList.events = POLLIN | POLLPRI;
//Continuously monitor the file descriptor
while(1)
{
if(poll(&pollList, (unsigned long) 1, -1))
{
//Sleep for 1 second - found it works best
sleep(1);
//Ready to read
bytesRead = read(fdCadet, &bufferBytes, 2048);
//Determine how many bytes were read
if(bytesRead < 0)
{
pthread_mutex_lock(&stdIO);
std::cerr << "\nREAD ERROR: System reported bytes were ready to be read, but read() returned an error code: "
<< "\n\t"
<< strerror(errno)
<< std::endl;
pthread_mutex_unlock(&stdIO);
}
else if(bytesRead == 0)
{
pthread_mutex_lock(&stdIO);
std::cout << "\nPOTENTIAL ERROR: System reported bytes were ready to be read, but no bytes were found."
<< std::endl;
pthread_mutex_unlock(&stdIO);
}
else
{
//Print out string in hexadecimal
pthread_mutex_lock(&stdIO);
printf("\nSerial Response: ");
for(i = 0; i < bytesRead; i++)
{
printf("%02X ", bufferBytes[i]);
}
std::cout << "\nBytes Received: "
<< bytesRead
<< std::endl;
pthread_mutex_unlock(&stdIO);
} // END IF bytesRead
} //End IF poll()
} //End while
} // End Function
Now I don't believe anything is wrong with this function, being that it's simply reading whatever came into the tty file and printing it out. It's not (or at least shouldn't) be manipulating the data.
The third code snippet is just Arduino code (used for testing) - it simply echoes whatever data comes in.
void setup()
{
Serial.begin(57600);
}
void loop()
{
if(Serial.available())
Serial.write(Serial.read());
}
I doubt anything is wrong with this either - but I can't say I know the Arduino functions that well.
And finally, the code below is where I suspect I have a problem. It's my setup code for the 'dev/tty*' serial settings.
void setInterfaceAttribs(int fd, int baud, int databits, int stopbits, int parity, std::string flow)
{
//Local Variables
speed_t baudRate = B57600;
//Allocate termios structure
struct termios tty;
memset(&tty, 0, sizeof(tty));
//Grab attributes and check for errors
if(tcgetattr(fd, &tty) != 0)
{
std::cerr << "\nError encountered when calling tcgetattr: "
<< errno
<< "\nThe program will now exit."
<< std::endl;
exit(-1);
}
//Set to raw mode
cfmakeraw(&tty); //Some of the commands below will be redundant or may modify certain values
//Set BAUD Rate
switch(baud)
{
case 9600:
baudRate = B9600;
break;
case 19200:
baudRate = B19200;
break;
case 38400:
baudRate = B38400;
break;
case 57600:
baudRate = B57600;
break;
case 115200:
baudRate = B115200;
break;
case 230400:
baudRate = B230400;
break;
case 460800:
baudRate = B460800;
break;
case 921600:
baudRate = B921600;
break;
default:
baudRate = B9600;
break;
}
cfsetospeed(&tty, speed);
cfsetispeed(&tty, speed);
//Set number of data bits
tty.c_cflag &= ~CSIZE;
switch(databits)
{
case 5:
tty.c_cflag |= CS5;
break;
case 6:
tty.c_cflag |= CS6;
break;
case 7:
tty.c_cflag |= CS7;
break;
case 8:
tty.c_cflag |= CS8;
break;
default:
tty.c_cflag |= CS8;
break;
}
//Set number of stop bits
switch(stopbits)
{
case 1:
tty.c_cflag &= ~CSTOPB;
break;
case 2:
tty.c_cflag |= CSTOPB;
break;
default:
tty.c_cflag |= CSTOPB;
break;
}
//Set Parity Encoding
switch(parity)
{
case 0: //Even
tty.c_cflag &= ~(PARENB | PARODD);
tty.c_cflag |= PARENB;
break;
case 1: //Odd
tty.c_cflag &= ~(PARENB | PARODD);
tty.c_cflag |= (PARENB | PARODD);
break;
case 2: //None
tty.c_cflag &= ~(PARENB | PARODD);
break;
default:
tty.c_cflag &= ~(PARENB | PARODD);
break;
}
//Set flow control
if( (!flow.compare("true")) || (!flow.compare("t")) )
tty.c_cflag |= CRTSCTS;
else
tty.c_cflag &= ~CRTSCTS;
//NOTE: (XON/XOFF) always disabled
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
//Finish some stuff up...
tty.c_cflag |= (CLOCAL | CREAD); //Ignore modem controls, enable read
tty.c_iflag &= ~IGNBRK; //disable break processing
tty.c_lflag = 0; //no signalling characters, etc
tty.c_cc[VMIN] = 0; //Read doesn't block
tty.c_cc[VTIME] = 5; //0.5 second timeout
//tty.c_ispeed = baud;
//tty.c_ospeed = baud;
//Flush, then apply attributes
tcflush(fd, TCIOFLUSH);
if(tcsetattr(fd, TCSANOW, &tty) != 0)
{
std::cerr << "\nError encountered when calling tcsetattr: "
<< errno
<< "\nThe program will now exit."
<< std::endl;
exit(-1);
}
return;
}
The reason I suspect this code is because of the settings I see when I run stty -F /dev/ttyACM1
, where /dev/ttyACM1
is the file my Arduino comes up as.
By default, the settings should be 57600 BAUD, 8N1, raw, and no flow control. However, this is what comes up:
stty -F /dev/ttyACM1
speed 0 baud; line = 0;
min = 1; time = 0;
-brkint -icrnl -imaxbel
-opost -onlcr
-isig -icanon -iexten -echo -echoe -echok -echoctl -echoke
I'm not sure what's going on. Any input would be appreciated.
stty
, the settings are what I would want. That is, speed is 57600 baud when using GTKterm. According to the documentation B0 is used for modem control. In any case, this is where I'm at. – Mlagma