1. Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.
    Dismiss Notice

C programming. Using termios

Discussion in 'Microcontrollers' started by Les Jones, Jan 1, 2018.

  1. Les Jones

    Les Jones Well-Known Member

    Joined:
    May 15, 2015
    Messages:
    1,580
    Likes:
    199
    Location:
    Lancashire UK
    ONLINE
    I am trying to get a program which is designed to record data received over serial link via HC12 modules (433 mhz serial link) to behave as I want. As there is a possibility of data being lost over the wireless link I would like to get the termios receive function to time out if it does not receive data within a certain anount of time so that the program does not hang waiting for data. I am using a Raspberry Pi for the data logging. The request for data from a remote sensor is initiated by the Raspberry Pi sendin a "#" character followed by a letter. (The sensor address) The sensor then responds with a ASCII text string. I am pretty useless at "C" programming so there are probably ways the the code could be improved. The is the code in it's present state.
    Code (text):

    /*
     * 12/03/17 Code being added to write the data to a file.
     * 13/03/17 Being modified to extract raw humidity an temperature
     * 13/03/17 Being modified for voltage monitor
     * 14/03/17 Being modified to enter time between readings and number of readings.
     * 18/03/17 Serial input buffer now beinig flushed at and of read
     * with "tcflush(fd, TCIFLUSH);" instruction
     * 23/12/17 Moving TCIFLUSH) to just before sending # character.
     * 24/12/17 Added a 500 mS delay between sending station address and
     *          reading input string.This prevents it only reading the
     *          first 8 bytes of the input string.
     *          Added a 10 mS delay at the start of the main "do"
     *          loop before the "displayDecoderValues(fd);" This seems to stop the
     *          program hanging intermittently.
     * 31/12/17 Attempting to test if the first character in "serialBuff_i" is 0x00
     *          If so then request a new read of data from remote station.
     *
     * Test010b.c
     *
     *
     */

    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <termios.h>
    #include <errno.h>


    void writeBytes(int descriptor, int count);
    void readBytes(int descriptor, int count);
    void displayDecoderValues(int fd);

    char serialBuff_i[30];           // Serial buffer sto store data for I/O
    char serialBuff_o[30];
    char outbuff[30];
    char vbuff[10];
    char F_name[25];           //Disk file name
    int rnum = 1;
    int S_time = 1;              // Time between readings
    int samples = 100;           // Number of readings to be taken.
    int main(int argc, char **argv)
    {
       int fd;                                       // File descriptor of port we will talk to
       char *portName = "/dev/ttyAMA0";       // Name of the UART port on the Raspberry pi
       struct termios options;    
       struct termios oldtio, newtio;
       char buf[255];
                           // Port options
     
       fd = open(portName, O_RDWR | O_NOCTTY);       // Open port for read and write not making it a controlling terminal
       if (fd == -1)
       {
           perror("openPort: Unable to open port ");   // If open() returns an error
       }
       tcgetattr(fd, &options);
       cfsetispeed(&options, B9600);           // Set baud rate
       cfsetospeed(&options, B9600);                
    //   cfmakeraw(&options);
       newtio.c_lflag = 0;   //Set non canonical
       options.c_cc[VTIME] = 10;   //struct termios
       options.c_cc[VMIN] = 10;
       tcflush(fd, TCIFLUSH);
       tcsetattr(fd, TCSANOW, &options); //End of UART setup
     
       usleep(10000);       // Sleep for UART to power up and set options


       printf ("Name of file to hold readings ? ");
       scanf ("%s", F_name);

       printf ("Time between readings ? (Integer minutes.) ");
       scanf ("%d", &S_time);

       printf ("\nNumber of readings ? ");
       scanf ("%d", &samples);
         
       int a = 0;

       do
       {
       printf("new   ");
       usleep(10000);
       displayDecoderValues(fd);

       serialBuff_o[0] = 0x0A;
       serialBuff_o[1] = 0x0D;    
     
       writeBytes(fd, 2);
     
    //   sleep(S_time * 60);
       sleep(S_time);

       a++;
       } while (a < samples);
       close(fd);
       printf ("Exit\r\n");
     
       return 0;
    }

    void writeBytes(int descriptor, int count) {
       if ((write(descriptor, serialBuff_o, count)) == -1) {           // Send data out
           perror("Error writing");
           close(descriptor);                       // Close port if there is an error
           exit(1);
       }
    }

    void readBytes(int descriptor, int count) {
       if (read(descriptor, serialBuff_i, count) == -1) {               // Read back data into buf[]
           perror("Error reading ");
           printf("Read error");
           close(descriptor);                           // Close port if there is an error
           exit(1);
       }
    }


    void displayDecoderValues(int fd) {
       tcflush(fd, TCIFLUSH);
       serialBuff_o[0] = ' ';       // space character (To wake up UART)
       serialBuff_o[1] = '#';       // # character
       serialBuff_o[2] = 'D';      // Station ID                                                            // Command to return encoder values
       writeBytes(fd, 3);

       usleep(100000);
       printf("Now about to read data \r\n");
       readBytes(fd, 12);       //

       usleep(500000);
       printf("Data read \r\n");


       if (serialBuff_i[0] ==0x00)
       {
       printf("Null character at start of input buffer \r\n");
       }
     
    //   sleep(2);
       strcpy (outbuff, serialBuff_i);

       outbuff[16] = 0x00; // Write 0x00 to terminate string

       printf(" \r\n");
       printf (outbuff); //Print to screen

       vbuff[0] = outbuff[0];
       vbuff[1] = outbuff[1];
       vbuff[2] = outbuff[2];
       vbuff[3] = outbuff[3];
       vbuff[4] = outbuff[4];
       vbuff[5] = outbuff[5];
       vbuff[6] = 0x2C;   // Comma character
       vbuff[7] = 0x00;       //End of string marker

       printf ("\n%d,", rnum);

       printf (vbuff);

    // Setup for writing to disk
       FILE *fp;
       int value;
       fp = fopen (F_name , "ab"); // "ab" append binary

       if (fp)
       {

       fprintf (fp,"\n%d,", rnum);
       rnum ++;

       fprintf (fp, vbuff);

       fclose (fp);   //close disk file
       }
     
       printf(" \r\n");
       printf("Next   ");

       fflush(stdout);                   // Flush output
    }

     
    When the program hangs if data is list is the line " readBytes(fd, 12); " which is just after the line "printf("Now about to read data \r\n");" (These printf lines were added to find out where the program was hanging.)

    Les.
     
  2. Pommie

    Pommie Well-Known Member Most Helpful Member

    Joined:
    Mar 18, 2005
    Messages:
    10,567
    Likes:
    394
    Location:
    Brisbane Australia
    Not too versed in uart programming in linux but your timeout value is set to 1 second.

    Can you try setting VTIME and VMIN both to zero and add a timeout to your readBytes routine.
    Code (text):

    unsigned char readBytes(int descriptor, int count){
    uint32_t timeOut;
    uint8_t received=0,temp,error=FLASE;
    timeOut=millis()+100;            //allow 1/10th of a second
        while(received<count and error==FALSE){
            if(temp=read(descriptor, serialBuff_i + received, count - received) == -1)
                error=TRUE;
            else
                received+=temp;
            if(millis()>timeOut)
                error=TRUE;
        }
        return(error);
    }
     
    This will at least time out but you will need to change the calling code to check for an error and resend etc.

    I'm assuming that millis() exists and returns a 32 bit integer.

    The above is not tested and I'm not sure if you can add a uint8 to a char pointer (serialBuff_i + received) without getting an error.

    Mike.
     
  3. Les Jones

    Les Jones Well-Known Member

    Joined:
    May 15, 2015
    Messages:
    1,580
    Likes:
    199
    Location:
    Lancashire UK
    ONLINE
    Hi Mike,
    Thanks for the suggested changes in the code. I will try them and let you know if it solves the problem. I have had lots of problems with the termios read routine. I have tried to find some good documentation on termios to try to understand the exact meaning of the parameters but failed. I find "C" difficult to understand compared with assembler. With assembler it is easy to find out exactly what each instruction does from the data sheet.

    Les.
     
  4. dave miyares

    Dave New Member

    Joined:
    Jan 12, 1997
    Messages:
    2
    Likes:
    -10


     
  5. Ian Rogers

    Ian Rogers User Extraordinaire Forum Supporter Most Helpful Member

    Joined:
    Mar 28, 2011
    Messages:
    9,673
    Likes:
    947
    Location:
    Rochdale UK
    I take it you found the linux manual..

    http://man7.org/linux/man-pages/man3/termios.3.html

    On reading it it looks pretty much the same as Borlands old uart library....

    The timeout is primarily a "reset" so the comms can re-sync.. If your packet has 10 characters at 9600 baud your time out can be 20mS, if its too long you may miss the next packet.

    I have used a timer in the past to keep the com port clear after unsuccessful transmissions.. Using a timer stops the uart functions from hanging...
     
  6. Les Jones

    Les Jones Well-Known Member

    Joined:
    May 15, 2015
    Messages:
    1,580
    Likes:
    199
    Location:
    Lancashire UK
    ONLINE
    Hi Ian,
    I did find that document or something similar but I was looking for a better definition of timeout in the definition "VTIME Timeout in deciseconds for noncanonical read (TIME)" I did not know if it was how long was allowed between the first character received and the last character received or the the time before it was considered an error condition. I will explain what was happening. First this is the setup. The remote sensor (A PIC12F1840 and a HC12 in the garage.) which is waiting to receive a "#" character. It then waits a short time to receive its address character. (A letter "D" in this case) It then takes a voltage reading using the ADC and converts it to an ASCII text string. (This is in the form "12.128 Volts" followed by a space charcter and C/R, L/F) If it does not receive this character it goes back to waiting for a "#" character. The monitor program is running on a Raspberry Pi with an HC12 in my computer room. Also in the computer room is a PC with an HC12 running Tera Term monitoring traffic on the radio link. When the program hangs on the Raspberry Pi The PC has displayed the #D sent by the R Pi followed by the text string from the remote sensor. If any character is sent from the PC it wakes up the R Pi but it replaces the first character of the text string with the character sent from the PC. So for example 12.128 Volts is displayed on the R Pi as x2.128 Volts. I was hoping to get the readbytes routine to drop though with an error so I could send another #D to get the remote sensor to send another reading. I am going to try to get Mike's version of the readbytes routine to work.

    Les.
     
  7. Ian Rogers

    Ian Rogers User Extraordinaire Forum Supporter Most Helpful Member

    Joined:
    Mar 28, 2011
    Messages:
    9,673
    Likes:
    947
    Location:
    Rochdale UK
  8. dave miyares

    Dave New Member

    Joined:
    Jan 12, 1997
    Messages:
    2
    Likes:
    -10


     
  9. Ian Rogers

    Ian Rogers User Extraordinaire Forum Supporter Most Helpful Member

    Joined:
    Mar 28, 2011
    Messages:
    9,673
    Likes:
    947
    Location:
    Rochdale UK
    Me personally!!
    When I do this, I send from the remote.... This means the remote can sleep.

    Remote wakes... Sends ID Data.... CRLF... Back to sleep.

    If you have several remotes, then set the wake time slightly different to avoid cross talk!!
     
  10. Les Jones

    Les Jones Well-Known Member

    Joined:
    May 15, 2015
    Messages:
    1,580
    Likes:
    199
    Location:
    Lancashire UK
    ONLINE
    Hi Mike and Ian,
    Mike, I have tried to replace the "readbytes" routine in my program with your suggested routine but I can't get the line "while(received<count and error==FALSE){" to compile. My knowledge ov "C" programming is so poor that I can't see what is wrong. Mike and Ian, I think I should try going through Nigels "C" tutorials so that I have more idea about what I am trying to do. I think I must be like one of the people asking for help on some complex electronics when they don't even know ohms law.

    Les.
     
  11. Pommie

    Pommie Well-Known Member Most Helpful Member

    Joined:
    Mar 18, 2005
    Messages:
    10,567
    Likes:
    394
    Location:
    Brisbane Australia
    Sorry Les, my fault. I've been using VB and it crept into the above.
    The line should be "while(received<count && error==FALSE){".

    Mike.
     
  12. Ian Rogers

    Ian Rogers User Extraordinaire Forum Supporter Most Helpful Member

    Joined:
    Mar 28, 2011
    Messages:
    9,673
    Likes:
    947
    Location:
    Rochdale UK
    I do that..... A lot!!! Now I'm using Pascal, I'm getting other stupid errors... ie ":=".. Gets on me T**Ts...
     
  13. Les Jones

    Les Jones Well-Known Member

    Joined:
    May 15, 2015
    Messages:
    1,580
    Likes:
    199
    Location:
    Lancashire UK
    ONLINE
    Hi Mike and Ian,
    That modified version of the line of code cleared the error message the originl caused. I have now found that I can't get the "millis" function to work on the Raspberry Pi.The documentation that I found suggested that it should work with the header file "wiringPi.h" but adding that did not fix the problem. I think I will abandon this idea until I manage to learn enough about "C" programming to understand what I am doing. I did try looking at the link to Nigel Goodwin's tutorial but it is aimed at programming PICs
    Thanks to you and Ian for your help.

    Les.
     
  14. Les Jones

    Les Jones Well-Known Member

    Joined:
    May 15, 2015
    Messages:
    1,580
    Likes:
    199
    Location:
    Lancashire UK
    ONLINE
    Update on progress.
    I found this link which gives a good explanation of "VMIN" and "VTIME" This enabled me to modify the program so it did not hang when data was lost ofer the radio link. It now does a retry if data is lost.
    This is the modified version.
    Code (text):

    /*
     * 12/03/17 Code being added to write the data to a file.
     * 13/03/17 Being modified to extract raw humidity an temperature
     * 13/03/17 Being modified for voltage monitor
     * 14/03/17 Being modified to enter time between readings and number of readings.
     * 18/03/17 Serial input buffer now beinig flushed at and of read
     * with "tcflush(fd, TCIFLUSH);" instruction
     * 23/12/17 Moving TCIFLUSH) to just before sending # character.
     * 24/12/17 Added a 500 mS delay between sending station address and
     *          reading input string.This prevents it only reading the
     *          first 8 bytes of the input string.
     *          Added a 10 mS delay at the start of the main "do"
     *          loop before the "displayDecoderValues(fd);" This seems to stop the
     *          program hanging intermittently.
     * 31/12/17 Attempting to test if the first character in "serialBuff_i" is 0x00
     *          If so then request a new read of data from remote station.
     *
     * Test009b.c
     *
     *
     */

    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <termios.h>
    #include <errno.h>
    #include <wiringPi.h>
    #include <time.h>


    void writeBytes(int descriptor, int count);
    // void readBytes(int descriptor, int count);
    void displayDecoderValues(int fd);

    char serialBuff_i[30];           // Serial buffer sto store data for I/O
    char serialBuff_o[30];
    char outbuff[30];
    char vbuff[10];
    char F_name[25];           //Disk file name
    int rnum = 1;
    int S_time = 1;              // Time between readings
    int samples = 100;           // Number of readings to be taken.
    //int FALSE = 0;
    //int TRUE = 1;

    int main(int argc, char **argv)
    {
       int wiringPiSetup(void);

       unsigned int millis;

       int fd;                                       // File descriptor of port we will talk to
       char *portName = "/dev/ttyAMA0";       // Name of the UART port on the Raspberry pi
       struct termios options;      
       struct termios oldtio, newtio;
       char buf[255];
                           // Port options
       
       fd = open(portName, O_RDWR | O_NOCTTY);       // Open port for read and write not making it a controlling terminal
       if (fd == -1)
       {
           perror("openPort: Unable to open port ");   // If open() returns an error
       }
       tcgetattr(fd, &options);
       cfsetispeed(&options, B9600);           // Set baud rate
       cfsetospeed(&options, B9600);                  
       cfmakeraw(&options);
       newtio.c_lflag = 0;   //Set non canonical
       options.c_cc[VTIME] = 10;   //struct termios
       options.c_cc[VMIN] = 0;  
       tcflush(fd, TCIFLUSH);
       tcsetattr(fd, TCSANOW, &options); //End of UART setup
       
       usleep(10000);       // Sleep for UART to power up and set options


       printf ("Name of file to hold readings ? ");
       scanf ("%s", F_name);

       printf ("Time between readings ? (Integer minutes.) ");
       scanf ("%d", &S_time);

       printf ("\nNumber of readings ? ");
       scanf ("%d", &samples);
           
       int a = 0;  

       do  
       {
       printf("new   ");
       usleep(10000);
       displayDecoderValues(fd);
       

       serialBuff_o[0] = 0x0A;
       serialBuff_o[1] = 0x0D;                                                           // Command to return encoder values
       
       writeBytes(fd, 2);
       
    //   sleep(S_time * 60);
       sleep(S_time);

       a++;  
       } while (a < samples);
       close(fd);
       printf ("Exit\r\n");  
       
       return 0;
    }

    void writeBytes(int descriptor, int count) {
       if ((write(descriptor, serialBuff_o, count)) == -1) {                           // Send data out  
           perror("Error writing");
           close(descriptor);                                                           // Close port if there is an error
           exit(1);
       }
    }

     void readBytes(int descriptor, int count) {
       if (read(descriptor, serialBuff_i, count) == -1) {                               // Read back data into buf[]
           perror("Error reading ");
           printf("Read error");
           close(descriptor);                                                           // Close port if there is an error
           exit(1);
       }
    }



    void displayDecoderValues(int fd) {
    Tryagain:
       tcflush(fd, TCIFLUSH);
       serialBuff_o[1] = '#';       // # character
       serialBuff_o[2] = 'D';      // Station ID                                                            // Command to return encoder values  
       writeBytes(fd, 3);

       usleep(100000);
       printf("Now about to read data \r\n");
       usleep(300000);
       readBytes(fd, 12);
               //
       if (serialBuff_i[0] ==0x00)
       {
       printf("Missed reading \r\n");
       goto Tryagain;
       }
    //   usleep(500000);
    //   printf("Data read \r\n");    

       strcpy (outbuff, serialBuff_i);
       serialBuff_i[0] = 0x00;  // clear string ???  

       outbuff[16] = 0x00; // Write 0x00 to terminate string

       printf(" \r\n");
       printf (outbuff); //Print to screen
       

       vbuff[0] = outbuff[0];
       vbuff[1] = outbuff[1];
       vbuff[2] = outbuff[2];
       vbuff[3] = outbuff[3];
       vbuff[4] = outbuff[4];
       vbuff[5] = outbuff[5];
       vbuff[6] = 0x2C;   // Comma character
       vbuff[7] = 0x00;       //End of string marker

       printf ("\n%d,", rnum);

       printf (vbuff);

    // Setup for writing to disk
       FILE *fp;
       int value;
       fp = fopen (F_name , "ab"); // "ab" append binary

       if (fp)
       {

       fprintf (fp,"\n%d,", rnum);
       rnum ++;

       fprintf (fp, vbuff);

       fclose (fp);   //close disk file
       }
       
       printf(" \r\n");
       printf("Next   ");

       fflush(stdout);                                                                   // Flush output to ensure that data is displayed on the screen
    }

     
    Les.
     

Share This Page