3

I want to read the content of a pen-drive data connected to a FTDI board through a c program. I have the following code using which I can read partial data but that also happens sometimes not every time I connect the board to PC. Can you please tell me what changes should be made to the code

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<errno.h>
#include <termios.h>
#include <unistd.h>
#include<string.h>
int n = 0;
struct termios tty;
struct termios tty_old;


main()
{
    unsigned char buf[100];
    int fd;
    fd= open("/dev/ttyUSB0", O_RDWR| O_NOCTTY);

    if(fd>0)
    {
         printf("Port opened\n");
    }
    memset (&tty, 0, sizeof tty);
    printf("set attributes\n");
    /* Error Handling */
    if ( tcgetattr ( fd, &tty ) != 0 )
    {
        printf("Error from tcgetattr:%d \n",strerror(errno));
    }

    /* Save old tty parameters */
    tty_old = tty;
    memset(&tty,0,sizeof(tty));
        tty.c_iflag=0;
        tty.c_oflag=0;
    tty.c_lflag=0;


    /* Set Baud Rate */
    cfsetospeed (&tty, (speed_t)B9600);
    cfsetispeed (&tty, (speed_t)B9600);

    /* Setting other Port Stuff */
    tty.c_cflag     &=  ~PARENB;        // Make 8n1
    tty.c_cflag     &=  ~CSTOPB;
    tty.c_cflag     &=  ~CSIZE;
    tty.c_cflag     |=  CS8;

    tty.c_cflag     &=  ~CRTSCTS;       // no flow control
    tty.c_cc[VMIN]      =   1;                  // read doesn't block
    tty.c_cc[VTIME]     =   5;                  // 0.5 seconds read timeout
    tty.c_cflag     |=  CREAD | CLOCAL;     // turn on READ & ignore ctrl lines

    /* Make raw */
    cfmakeraw(&tty);


    /* Flush Port, then applies attributes */
    tcflush( fd, TCIFLUSH );
    if (tcsetattr (fd, TCSANOW, &tty) != 0)
    {
        printf("Error from tcsetattr:%d \n");
    }


    while(1)
    {
        printf("Do read and write\n");
        n = read(fd,&buf, sizeof buf);
        if (n < 0)
        {
            printf("Error reading:\n ");
            break;
        }
        else if (n == 0)
        {
                printf("Read nothing!\n");
            break;
        }
        else
        {
            buf[n]='\0';
            printf("%s",buf);
        }
    }   
}
Appie
  • 129
  • 1
  • 1
  • 9
  • 2
    I assume you're using an FTDI USB-to-Serial converter. My experience is that it is important to first plug the converter into the USB port and only _then_ connect the peripherial to the converter. This is because the operating system tries to do some handshake when the serial device node shows up, which might confuse your peripherial. Not sure whether this is the case here. – Jonas Schäfer Nov 23 '13 at 09:46
  • Should I add delay before reading? – Appie Nov 23 '13 at 10:16
  • No, you don't need a delay (I think). Only make sure you *first* plug the FTDI into the USB and *then* connect your pen-drive. – Jonas Schäfer Nov 23 '13 at 10:19
  • Your suggestion worked only on the first trial. Any more idea? – Appie Nov 23 '13 at 10:30

2 Answers2

2

As you are using an FTDI USB-to-serial converter, care must be taken in which order you connect the devices.

After you connect the FTDI to the computer, the operating system sees a new serial device. Usually, this will make it try to handshake with the serial device (you can see that on some FTDI adapter boards on the blinking LEDs for rx/tx).

However, your peripherial might not be able to deal with that handshake and gets into an inconsistent or unknown (to you) state.

Thus it is important to first connect the FTDI to the computer and then connect the peripherial (your pen-drive) to the FTDI. This makes sure that the device does not see the handshake and your program can directly talk to it.

Jonas Schäfer
  • 20,140
  • 5
  • 55
  • 69
  • I tried your answer but got the output only for the first time ... for subsequent trials I'm not getting the expected answer – Appie Nov 23 '13 at 11:51
1

Check the timing. Run the code with no device connected: it will end because n==0. I suppose the program is running after the data has been sent and the OS was not receiving data because port was not open. When it's running ok is because you got the timing between starting program and switch on the device.

To avoid this, don't stop the loop when 0 is returned. Put a condition like a key being pressed or after some time running. And remove some printf's to avoid see too many messages on console.

Erick
  • 992
  • 6
  • 6
  • 1
    A read of 0 should not happen, unless you disconnect the USB device. So in fact first running the program and then connecting the device is a good plan. – Jonas Schäfer Nov 23 '13 at 09:45
  • But the serial is set up to non-blocking, with only half a second for read timeout. It will return 0 when no byte is found, which can happen when the device was faster than program starting, don't you think? – Erick Nov 23 '13 at 10:01
  • 1
    No, I think it will block: The manpage describes the interaction of both TIME and MIN being nonzero as: _TIME specifies the limit for a timer in tenths of a second. Once an initial byte of input becomes available, the timer is restarted after each further byte is received. read(2) returns either when the lesser of the number of bytes requested or MIN byte have been read, or when the inter-byte timeout expires. **Because the timer is started only after the initial byte becomes available, at least one byte will be read.**_ Admittedly, this is hidden well below the fields‘ description – Jonas Schäfer Nov 23 '13 at 10:09
  • I still don't agree since we are talking about non blocking read, a situation where you might need to pool the buffer and continue your job if still there are no bytes to process. Take a look [here](http://www.linux-mag.com/id/308/) which discuss about slow files, like a socket or a serial port are. – Erick Nov 23 '13 at 10:25
  • Hm, where in the code above do you see the file being put in non-blocking mode? – Jonas Schäfer Nov 23 '13 at 10:27
  • Ops, a read on the comment, but it's not totally true. With this setup read will wait for 1 byte, as VMIN=1. Sorry! – Erick Nov 23 '13 at 10:38
  • So, VMIN and VTIME are okay for the setup, I think. – Jonas Schäfer Nov 23 '13 at 10:48
  • Agreed. Again, sorry about the confusion. @Aparna, have you tried Jonas' answer? – Erick Nov 23 '13 at 10:57
  • I tried Jonas answer. but got the output only for the first time ... for subsequent trials I'm not getting the expected answer – Appie Nov 23 '13 at 11:39
  • 1
    I have no unix machine to test this code now, so could you try some things? 1) you're not closing the port after the loop. This could explain why it works first time and not anymore. 2) take a look at [Serial Programming Howto](http://www.ibiblio.org/pub/linux/docs/HOWTO/Serial-Programming-HOWTO), specially on source code on "3.3. Asynchronous Input"; this will keep the port opened and you can try switch on and off the peripheral in order to see if data aquisition is the same on each time. – Erick Nov 23 '13 at 12:10
  • Very helpful!! Thank you JonasWielicki and Erick – Appie Nov 25 '13 at 06:29