Serial communication in C

15,470

The problem has been fixed!

Apparently, the device was waiting for an end-line terminator, which was not available in char write_buffer[] = "READ?";

Therefore when it received such a command, the bus would "hang" and in order for it to work again one would have to open the port again.

This also explains why the echo "READ?" > /dev/ttyUSB0 command worked - since it automatically outputs an end-line terminator.

I attach the working code below - thank you everyone for your commments and kind help!

  int fd;           //device file id
//------------------------------- Opening the Serial Port -------------------------------
    fd = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY);    // ttyUSB0 is the FT232 based USB2SERIAL Converter 
    if(fd == -1)                        // Error Checking 
    printf("Error while opening the device\n");
//---------- Setting the Attributes of the serial port using termios structure ---------
    struct termios SerialPortSettings;  // Create the structure                          
    tcgetattr(fd, &SerialPortSettings); // Get the current attributes of the Serial port
// Setting the Baud rate
  cfsetispeed(&SerialPortSettings,B19200); // Set Read  Speed as 19200                       
    cfsetospeed(&SerialPortSettings,B19200); // Set Write Speed as 19200                       

    SerialPortSettings.c_cflag &= ~PARENB;   // Disables the Parity Enable bit(PARENB),So No Parity   
    SerialPortSettings.c_cflag &= ~CSTOPB;   // CSTOPB = 2 Stop bits,here it is cleared so 1 Stop bit 
    SerialPortSettings.c_cflag &= ~CSIZE;    // Clears the mask for setting the data size             
    SerialPortSettings.c_cflag |=  CS8;      // Set the data bits = 8                                 
    SerialPortSettings.c_cflag &= ~CRTSCTS;       // No Hardware flow Control                         
    SerialPortSettings.c_cflag |= CREAD | CLOCAL; // Enable receiver,Ignore Modem Control lines        
    SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY);  // Disable XON/XOFF flow control both i/p and o/p 
    SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);  // Non Cannonical mode 
    SerialPortSettings.c_oflag &= ~OPOST;//No Output Processing
// Setting Time outs 
    SerialPortSettings.c_cc[VMIN] = 13; // Read at least 10 characters 
    SerialPortSettings.c_cc[VTIME] = 0; // Wait indefinetly  

    if((tcsetattr(fd,TCSANOW,&SerialPortSettings)) != 0) // Set the attributes to the termios structure
    printf("Error while setting attributes \n");
    //------------------------------- Read data from serial port -----------------------------

    char read_buffer[32];   // Buffer to store the data received              
    int  bytes_read = 0;    // Number of bytes read by the read() system call 
  int bytes_written = 0;  // Number of bytes written
    int i = 0;

    tcflush(fd, TCIFLUSH);   // Discards old data in the rx buffer            
//Device intialization

  char write_buffer[]="READ? \n ";
  bytes_written=write(fd,&write_buffer,sizeof(write_buffer));


  bytes_read = read(fd,&read_buffer,32); // Read the data                   

    for(i=0;i<13;i++)    //printing only the needed characters
    printf("%c",read_buffer[i]);
    close(fd); // Close the serial port
Share:
15,470
Admin
Author by

Admin

Updated on June 04, 2022

Comments

  • Admin
    Admin almost 2 years

    I am trying to write a simple application to read out a current value from a Keithley 6485 picoammeter, connected via serial communication (RS232<->USB) on linux.

    Currently, such a value can be retrieved by doing all the needed initialization of the device and sending "READ?" to it: echo "READ?" > /dev/ttyUSB0. Then if cat /dev/ttyUSB0 has been listening, I get the following output: -2.250416E-14A,+8.320175E+03,+0.000000E+00,of which the first number is the desired value.

    To be able to output the value I use the following code using termios libraries:

        /*====================================================================================================*/
        /* Serial Port Programming in C (Serial Port Read)                                                    */
    /* Non Cannonical mode                                                                                */
    /*----------------------------------------------------------------------------------------------------*/
        /* Program reads a string from the serial port at 9600 bps 8N1 format                                 */
    /* Baudrate - 9600                                                                                    */
    /* Stop bits -1                                                                                       */
    /* No Parity                                                                                          */
        /*----------------------------------------------------------------------------------------------------*/
    /* Compiler/IDE  : gcc 4.6.3                                                                          */
    /* Library       :                                                                                    */
    /* Commands      : gcc -o serialport_read serialport_read.c                                           */
    /* OS            : Linux(x86) (Linux Mint 13 Maya)(Linux Kernel 3.x.x)                                */                              
    /* Programmer    : Rahul.S                                                                            */
    /* Date          : 21-December-2014                                                                   */
    /*====================================================================================================*/
    
    /*====================================================================================================*/
    /* www.xanthium.in                                            */
    /* Copyright (C) 2014 Rahul.S                                                                         */
    /*====================================================================================================*/
    
    /*====================================================================================================*/
    /* Running the executable                                                                             */
    /* ---------------------------------------------------------------------------------------------------*/ 
    /* 1) Compile the  serialport_read.c  file using gcc on the terminal (without quotes)                 */
        /*                                                                                                    */
    /*  " gcc -o serialport_read serialport_read.c "                                                  */
    /*                                                                                                    */
        /* 2) Linux will not allow you to access the serial port from user space,you have to be root.So use   */
        /*    "sudo" command to execute the compiled binary as super user.                                    */
        /*                                                                                                    */
        /*       "sudo ./serialport_read"                                                                     */
    /*                                                                                                    */
    /*====================================================================================================*/
    
    /*====================================================================================================*/
    /* Sellecting the Serial port Number on Linux                                                         */
    /* ---------------------------------------------------------------------------------------------------*/ 
    /* /dev/ttyUSBx - when using USB to Serial Converter, where x can be 0,1,2...etc                      */
    /* /dev/ttySx   - for PC hardware based Serial ports, where x can be 0,1,2...etc                      */
        /*====================================================================================================*/
    
    /*-------------------------------------------------------------*/
        /* termios structure -  /usr/include/asm-generic/termbits.h    */ 
    /* use "man termios" to get more info about  termios structure */
    /*-------------------------------------------------------------*/
    
        #include <stdio.h>
        #include <fcntl.h>   /* File Control Definitions           */
        #include <termios.h> /* POSIX Terminal Control Definitions */
        #include <unistd.h>  /* UNIX Standard Definitions      */ 
        #include <errno.h>   /* ERROR Number Definitions           */
    
    void main(void)
        {
            int fd;/*File Descriptor*/
    
        printf("\n +----------------------------------+");
        printf("\n |        Serial Port Read          |");
        printf("\n +----------------------------------+");
    
        /*------------------------------- Opening the Serial Port -------------------------------*/
    
        /* Change /dev/ttyUSB0 to the one corresponding to your system */
    
            fd = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY);    /* ttyUSB0 is the FT232 based USB2SERIAL Converter   */
        //  fd = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY | O_NDELAY); /* ttyUSB0 is the FT232 based USB2SERIAL Converter   */
                                /* O_RDWR   - Read/Write access to serial port       */
                                /* O_NOCTTY - No terminal will control the process   */
                                /* Open in blocking mode,read will wait              */
    
    
    
            if(fd == -1)                        /* Error Checking */
                   printf("\n  Error! in Opening ttyUSB0  ");
            else
                   printf("\n  ttyUSB0 Opened Successfully ");
    
    
        /*---------- Setting the Attributes of the serial port using termios structure --------- */
    
        struct termios SerialPortSettings;  /* Create the structure                          */
    
        tcgetattr(fd, &SerialPortSettings); /* Get the current attributes of the Serial port */
    
        /* Setting the Baud rate */
        cfsetispeed(&SerialPortSettings,B19200); /* Set Read  Speed as 19200                       */
        cfsetospeed(&SerialPortSettings,B19200); /* Set Write Speed as 19200                       */
    
        /* 8N1 Mode */
        SerialPortSettings.c_cflag &= ~PARENB;   /* Disables the Parity Enable bit(PARENB),So No Parity   */
        SerialPortSettings.c_cflag &= ~CSTOPB;   /* CSTOPB = 2 Stop bits,here it is cleared so 1 Stop bit */
        SerialPortSettings.c_cflag &= ~CSIZE;    /* Clears the mask for setting the data size             */
        SerialPortSettings.c_cflag |=  CS8;      /* Set the data bits = 8                                 */
    
        SerialPortSettings.c_cflag &= ~CRTSCTS;       /* No Hardware flow Control                         */
        SerialPortSettings.c_cflag |= CREAD | CLOCAL; /* Enable receiver,Ignore Modem Control lines       */ 
    
    
        SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY);          /* Disable XON/XOFF flow control both i/p and o/p */
        SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);  /* Non Cannonical mode                            */
    
        SerialPortSettings.c_oflag &= ~OPOST;/*No Output Processing*/
    
        /* Setting Time outs */
        SerialPortSettings.c_cc[VMIN] = 13; /* Read at least 10 characters */
        SerialPortSettings.c_cc[VTIME] = 0; /* Wait indefinetly   */
    
    
        if((tcsetattr(fd,TCSANOW,&SerialPortSettings)) != 0) /* Set the attributes to the termios structure*/
            printf("\n  ERROR ! in Setting attributes");
        else
                    printf("\n  BaudRate = 19200 \n  StopBits = 1 \n  Parity   = none");
    
            /*------------------------------- Read data from serial port -----------------------------*/
    
        char read_buffer[32];   /* Buffer to store the data received              */
        int  bytes_read = 0;    /* Number of bytes read by the read() system call */
        int i = 0;
    
        tcflush(fd, TCIFLUSH);   /* Discards old data in the rx buffer            */
    
        bytes_read = read(fd,&read_buffer,32); /* Read the data                   */
    
        printf("\n\n  Bytes Rxed -%d", bytes_read); /* Print the number of bytes read */
        printf("\n\n  ");
        for(i=0;i<13;i++)    /*printing only the needed bytes*/
            printf("%c",read_buffer[i]);
    
        printf("\n +----------------------------------+\n\n\n");
    
        close(fd); /* Close the serial port */
    
        }
    

    Which outputs:

     +----------------------------------+
     |        Serial Port Read          |
     +----------------------------------+
      ttyUSB0 Opened Successfully 
      BaudRate = 19200 
      StopBits = 1 
      Parity   = none
    
      Bytes Rxed -13
    
      -2.864104E-14
     +----------------------------------+
    

    However, for it to be able to read the value, I have to echo the "READ?" command (or write to the device by using a write() function) every time when I want to know the value.

    How can I put both writing and reading to the same application in the most elegant way (e.g. without making of named pipes), as currently the read() function waits for anything to come from the device and during that time I can not send a "READ?" command from the same C code?


    EDIT: Apparently my writing procedure does not seem to work properly:

    Port settings:

        struct termios SerialPortSettings;  /* Create the structure                          */
    
        tcgetattr(fd, &SerialPortSettings); /* Get the current attributes of the Serial port */
    
        cfsetispeed(&SerialPortSettings,(speed_t)B19200); /* Set Read  Speed as 19200                       */
        cfsetospeed(&SerialPortSettings,(speed_t)B19200); /* Set Write Speed as 19200                       */
    
        SerialPortSettings.c_cflag &= ~PARENB;   /* Disables the Parity Enable bit(PARENB),So No Parity   */
        SerialPortSettings.c_cflag &= ~CSTOPB;   /* CSTOPB = 2 Stop bits,here it is cleared so 1 Stop bit */
        SerialPortSettings.c_cflag &= ~CSIZE;    /* Clears the mask for setting the data size             */
        SerialPortSettings.c_cflag |=  CS8;      /* Set the data bits = 8                                 */
    
        SerialPortSettings.c_cflag = ~CRTSCTS;       /* No Hardware flow Control                         */
        SerialPortSettings.c_cflag |= CREAD | CLOCAL; /* Enable receiver,Ignore Modem Control lines       */ 
    
    
    cfmakeraw(&SerialPortSettings);
    
    tcflush(fd,TCIFLUSH);
    
        SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY);          // Disable XON/XOFF flow control both i/p and o/p
        SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);  // Non Cannonical mode                           
        SerialPortSettings.c_oflag &= ~OPOST;//No Output Processing
    

    Writing:

        char write_buffer[] = "READ?";  /* Buffer containing characters to write into port       */ 
        int  bytes_written  = 0;    /* Value for storing the number of bytes written to the port */ 
    
        bytes_written = write(fd,&write_buffer,sizeof(write_buffer) -1);
    printf("%s written to ttyUSB0 \n",write_buffer);
        printf("%d Bytes written to ttyUSB0 \n", bytes_written);
        printf("+----------------------------------+\n\n");
    
        close(fd);/* Close the Serial port */
    

    Whenever this runs, I get:

    +----------------------------------+ 
    |        Serial Port Write         | 
    +----------------------------------+ 
    ttyUSB0 Opened Successfully 
    Attributes set 
    READ? written to ttyUSB0 
    5 Bytes written to ttyUSB0 
    +----------------------------------+
    

    But cat /dev/ttyUSB0 does not seem to see anything coming from the device. I have checked the similar question at:

    Linux C Serial Port Reading/Writing

    and can not find big differences in code - would that be a sign of wrong port settings or am I missing something?