0

So I searched around, and couldn't exactly find what I needed. I need help reading and writing binary data over a serial port, and would appreciate any advice you may have. Please note, I asked a question similar to this earlier when I was at a different stage of this project.

Below are programs. The first program opens a file "test.jpg", reads it in binary mode and stores the result in a buffer. It then closes the file, and is supposed to send that file over a serial port.

The second program creates a file called "testout.jpg", and is supposed to read in the data sent from the previous program.

I have a hunch that the problem in my code lies in the second program. Perhaps I need to use fread for that too? I tried, but I cannot figure out how to implement it for a serial port as I am relatively new to programming.

Many thanks for your time.

Serial write:

    #include <stdio.h>   /* Standard input/output definitions */
    #include <string.h>  /* String function definitions */
    #include <unistd.h>  /* UNIX standard function definitions */
    #include <fcntl.h>   /* File control definitions */
    #include <errno.h>   /* Error number definitions */
    #include <termios.h> /* POSIX terminal control definitions */
    #include <stdlib.h>

    int main()
        {
                //writing
                int writeport = open_port("/dev/ttyUSB0");

                //open file

                FILE *file;
                char *buffer;
                int fileLen;

                file = fopen("test.jpg", "rb");

                //get file size

                fseek(file, 0, SEEK_END);
                fileLen = ftell(file);
                fseek(file, 0, SEEK_SET);

                buffer = (char *)malloc(fileLen + 1);

                //read file contents

                fread(buffer, fileLen, 1, file);
                fclose(file);

                int n = write(writeport, buffer, fileLen + 1);
                if (n < 0)
                        fputs("write() of bytes failed!\n", stderr);

                //closing ports
                close(writeport);
        }

        int open_port(char str[])
    {
        int fd = open(str, O_RDWR | O_NOCTTY | O_NONBLOCK); // ?? NDELAY or NONBLOCK?

      if (fd == -1)
      {
                        perror("open_port: Unable to open /dev/ttyS0 - ");
      }
      else
                        fcntl(fd, F_SETFL, 0);

          struct termios options;
          tcgetattr(fd, &options); //this gets the current options set for the port

          // setting the options

          cfsetispeed(&options, B9600); //input baudrate
          cfsetospeed(&options, B9600); // output baudrate
          options.c_cflag |= (CLOCAL | CREAD); // ?? enable receicer and set local mode
          //options.c_cflag &= ~CSIZE; /* mask the character size bits */
          options.c_cflag |= CS8;    /* select 8 data bits */
          options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // choosing raw input
          options.c_iflag &= ~INPCK; // disable parity check
          options.c_iflag &= ~(IXON | IXOFF | IXANY); // disable software flow control
          options.c_oflag |= OPOST; // ?? choosing processed output
          options.c_cc[VMIN] = 0; // Wait until x bytes read (blocks!)
          options.c_cc[VTIME] = 0; // Wait x * 0.1s for input (unblocks!)

          // settings for no parity bit
          options.c_cflag &= ~PARENB;
          options.c_cflag &= ~CSTOPB;
          options.c_cflag &= ~CSIZE;
          options.c_cflag |= CS8;

          tcsetattr(fd, TCSANOW, &options); //set the new options ... TCSANOW specifies all option changes to occur immediately

      return (fd);
    }

Serial read:

    #include <stdio.h>   /* Standard input/output definitions */
    #include <string.h>  /* String function definitions */
    #include <unistd.h>  /* UNIX standard function definitions */
    #include <fcntl.h>   /* File control definitions */
    #include <errno.h>   /* Error number definitions */
    #include <termios.h> /* POSIX terminal control definitions */

    int main()
        {
                //reading      
                int readport = open_port("/dev/ttyUSB1");

        //open resultant file

        FILE *file;
        //system("rm testout.jpg");
        file = fopen("testout.jpg", "wb");

                //trying to read one character at a time
                char buff;
                int n = 1;

           while (n > 0)
           {
                n = read(readport, &buff, 1);
                //printf("%c", buff, buff);

                **//I tried these three methods, with little success**

                //fprintf(file, "%c", buff);
                //fwrite(&buff, 1, 1, file);
                //write(file, &buff, 1);
           }

                //closing ports
                close(readport);
                fclose(file);
        }

        int open_port(char str[])
    {
        int fd = open(str, O_RDWR | O_NOCTTY | O_NONBLOCK); // ?? NDELAY or NONBLOCK?

      if (fd == -1)
      {
                        perror("open_port: Unable to open /dev/ttyS0 - ");
      }
      else
                        fcntl(fd, F_SETFL, 0);

          struct termios options;
          tcgetattr(fd, &options); //this gets the current options set for the port

          // setting the options

          cfsetispeed(&options, B9600); //input baudrate
          cfsetospeed(&options, B9600); // output baudrate
          options.c_cflag |= (CLOCAL | CREAD); // ?? enable receicer and set local mode
          //options.c_cflag &= ~CSIZE; /* mask the character size bits */
          options.c_cflag |= CS8;    /* select 8 data bits */
          options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // choosing raw input
          options.c_iflag &= ~INPCK; // disable parity check
          options.c_iflag &= ~(IXON | IXOFF | IXANY); // disable software flow control
          options.c_oflag |= OPOST; // ?? choosing processed output
          options.c_cc[VMIN] = 0; // Wait until x bytes read (blocks!)
          options.c_cc[VTIME] = 0; // Wait x * 0.1s for input (unblocks!)

          // settings for no parity bit
          options.c_cflag &= ~PARENB;
          options.c_cflag &= ~CSTOPB;
          options.c_cflag &= ~CSIZE;
          options.c_cflag |= CS8;

          tcsetattr(fd, TCSANOW, &options); //set the new options ... TCSANOW specifies all option changes to occur immediately

      return (fd);
    }
tomlogic
  • 11,489
  • 3
  • 33
  • 59
capcom
  • 3,257
  • 12
  • 40
  • 50
  • You don't seem to have any error handling, Have you removed it to reduce the amount of code we need to read to understand your problem or do you simply not have it in your code (in which case you should at least check the the files/ports you open are valid and in good state!). The use of `O_NONBLOCK` suggests that you are not waiting for the port to be open (or am I reading it wrong?). – YePhIcK Jul 25 '12 at 12:43
  • `while (n > 0) { n = read(readport, &buff, 1); ` is wromg. Test the n immediately after the read(), and break out of the loop if (n <= 0). (n==0 is special: depends on blocking/non blocking) – wildplasser Jul 25 '12 at 12:47
  • @YePhlck Yes you are right about both those things. O_NONBLOCK was used because it wasn't working with NDELAY. – capcom Jul 25 '12 at 12:49
  • It appears you use O_NONBLOCK. if `(n=read(...) == -1)` you should check errno for EAGAIN/EWOULDBLOCK. – wildplasser Jul 25 '12 at 12:50
  • @wildplasser So would this be better: while (true) { if (n <= 0) break; else n = read(readport, &buff, 1);} Thanks! – capcom Jul 25 '12 at 12:51
  • NO!!! put the test *after* the `n=read(...);` you want to test the *new* n, not an old one.. Again: check errno if n == -1. – wildplasser Jul 25 '12 at 12:53
  • Okay, I did that. Is this what you meant: `while (1) { n = read(readport, &buff, 1); if (n == -1) printf("%s\n", strerror(errno)); if (n <= 0) break;}` Thanks. If so, I tried that, and I get the same result using fprintf. – capcom Jul 25 '12 at 13:07
  • See https://stackoverflow.com/questions/12437593/how-to-read-a-binary-data-over-serial-terminal-in-c-program Your write code is broken; see https://stackoverflow.com/questions/71524964/writting-0d-0a-insted-of-0a-when-i-tried-to-write-into-uart/71531204#71531204 Reading just one byte per syscall is very inefficient; see https://stackoverflow.com/questions/54392470/how-to-handle-buffering-serial-data/54394770#54394770 – sawdust Mar 25 '22 at 05:59

1 Answers1

1
file = fopen( "zname.jpg", "wb" );
while (1) { 
  n = read(readport, &buff, 1);
  if (n == -1) switch(errno) {
         case EAGAIN: /* sleep() */ 
            continue;
          ...
         default: goto quit;
         }
  if (n ==0) break;
  fputc(buff, file);
  }
quit:
   fclose (file);
...

Even better than sleep() and loop, would be to use select/poll. (You'd still have to check for EAGAIN)

wildplasser
  • 43,142
  • 8
  • 66
  • 109
  • 1
    I'm experimenting with your code, but am wondering whether I should be using fwrite and fclose, not write and close. Thanks. – capcom Jul 25 '12 at 13:40
  • I now see that the file was opened with fopen(), so you should use fwite() and fclos(). Instead of fwrite, you could also use fputc() BTW: I updated my answer. – wildplasser Jul 25 '12 at 13:54
  • Thanks for all your help. I think the problem may be a little more deep rooted and I will discuss it with one of my friends today. I'll update this question with my solution. – capcom Jul 25 '12 at 16:22