2
votes

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.

1
Does it work as expected if you use a simple terminal program to perfom the test as per your question?alk
@alk Sorry for the late reply. I tried it out with GTKterm, and the arduino is sending back the expected data. This, again, makes me suspect the tty port configuration code. When I check out the port using 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

1 Answers

1
votes

Well, I must say I feel quite dumb right now. However, if anyone is trying to figure out what's wrong with the code above, I feel that I should at least provide the answer - it is really simple.

I'll highlight the error, and it'll probably hit you:

.
.
.
default:
        baudRate = B9600;
        break;
    }

    cfsetospeed(&tty, speed);
    cfsetispeed(&tty, speed);

The variable was wrong. I copied and pasted some old code I had, and apparently I used a different variable name than last time (speed should be baudRate). In any case, the code is working now. At the very least, I saw an interesting side effect that I won't forget.