Thread: Wirte and read serial port

  1. #61
    TEIAM - problem solved
    Join Date
    Apr 2012
    Location
    Melbourne Australia
    Posts
    1,907
    You are using "void main" - Have you seen Salem's avatar? Change it to "int main" and return 0 at the end.
    Fact - Beethoven wrote his first symphony in C

  2. #62
    Registered User
    Join Date
    Apr 2013
    Posts
    66
    Quote Originally Posted by Click_here View Post
    You are using "void main" - Have you seen Salem's avatar? Change it to "int main" and return 0 at the end.
    Changed it already but still the output data i get sometimes correct and sometimes not correct. hmm.

  3. #63
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    The way i clear my buffer, is that a good way?
    Where are you clearing your buffer? I'm not talking about your character buffer, I'm talking about the "hardware" buffer. You do know that both the operating system and the hardware usually buffer this port, right? To clear these buffers after you initialize the port you call tcflush().

    Code:
    tcflush(fd, TCIOFUSH); // Clear both the input and output buffers.
    comment on my code below:
    What's new to comment about? You haven't changed anything. You're still not checking if read() actually read anything. Please note this quote from this tutorial: Serial Programming Guide for Linux

    Reading Data from the Port

    Reading data from a port is a little trickier. When you operate the port in raw data mode, each read(2) system call will return the number of characters that are actually available in the serial input buffers. If no characters are available, the call will block (wait) until characters come in, an interval timer expires, or an error occurs. The read function can be made to return immediately by doing the following:

    fcntl(fd, F_SETFL, FNDELAY);

    The FNDELAY option causes the read function to return 0 if no characters are available on the port. To restore normal (blocking) behavior, call fcntl() without the FNDELAY option:

    fcntl(fd, F_SETFL, 0);
    Changed it already but still the output data i get sometimes correct and sometimes not correct. hmm.
    What is this supposed to mean? You'll need to provide much more information. Does your external hardware have a method of outputting a predetermined string so you can check the communications? Can you tell us what type of hardware your trying to interface to? The manufacturer, and if the board is mass produced the model and part number of the device you're using?




    Jim

  4. #64
    Registered User
    Join Date
    Apr 2013
    Posts
    66
    Quote Originally Posted by jimblumberg View Post
    Where are you clearing your buffer? I'm not talking about your character buffer, I'm talking about the "hardware" buffer. You do know that both the operating system and the hardware usually buffer this port, right? To clear these buffers after you initialize the port you call tcflush().

    Code:
    tcflush(fd, TCIOFUSH); // Clear both the input and output buffers.

    What's new to comment about? You haven't changed anything. You're still not checking if read() actually read anything. Please note this quote from this tutorial: Serial Programming Guide for Linux




    What is this supposed to mean? You'll need to provide much more information. Does your external hardware have a method of outputting a predetermined string so you can check the communications? Can you tell us what type of hardware your trying to interface to? The manufacturer, and if the board is mass produced the model and part number of the device you're using?




    Jim

    Hello jim, the problem seems to be solved already. I did the input and output clear buffer and its settled. Now i have another problem, below is my code to print the output from the device into a text file:

    Code:
     fp = fopen("results_eigenvalue.txt", "w");   /*open and write into existing file*/
    
     for(i=0;i<207;i++)
    {
     result = read(mainfd, &chout, sizeof(chout));          /* Read character from ABU (Auto buffering Unit) */
     if ( result == -1 ) { perror("error: read"); }
      if ( i >= 8){
        fprintf(fp,"%02x ",chout); }
    
    }
    Problem is the result i get is below:

    f5 23 00 c4 00 00 e7 f5 f5 00 00 00 29 (for example)

    What i need is some thing like below:

    \xf5\x23\xc4\x00\x00\xe7\xf5\xf5\x00\x00\x00\x29

    ==> I tried to do this "fprintf(fp,"\x%02x ",chout);", but i get this error below:

    error: \x used with no following hex digits

    Any suggestion?

  5. #65
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    The '\' is considered a special character, called the escape character. To print this special character you must use the escape character to tell the compiler you mean the character '\' not this special character so you need to use two '\' characters to print this character "\\".

    Jim

  6. #66
    Registered User
    Join Date
    Apr 2013
    Posts
    66
    Quote Originally Posted by jimblumberg View Post
    The '\' is considered a special character, called the escape character. To print this special character you must use the escape character to tell the compiler you mean the character '\' not this special character so you need to use two '\' characters to print this character "\\".

    Jim
    So basically i cant have this kind of output? \xf5\x23\xc4\x00\x00\xe7\xf5\xf5\x00\x00\x00\x29?

  7. #67
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    So basically i cant have this kind of output? \xf5\x23\xc4\x00\x00\xe7\xf5\xf5\x00\x00\x00\x29?
    Where did I say anything like that. You need to re-read my last post. You need to escape this escape character.

    Jim

  8. #68
    Registered User
    Join Date
    Apr 2013
    Posts
    66
    Quote Originally Posted by jimblumberg View Post
    Where did I say anything like that. You need to re-read my last post. You need to escape this escape character.

    Jim
    Oh man you're awesome dude..thanks for helping me..^^

  9. #69
    Registered User
    Join Date
    Apr 2013
    Posts
    66
    Quote Originally Posted by jimblumberg View Post
    Where did I say anything like that. You need to re-read my last post. You need to escape this escape character.

    Jim
    Hello Jim, now i have a big problem, hope you'll bear with me to solve this.

    1.) i'm sending hex command to my device/port like the code below:

    Code:
    result2 = write(mainfd,"\xF5\x23\x00\x00\x00\x00\x23\xF5",8);
     if ( result2 == -1 ) { perror("error: write"); }
     if ( result2 != 8 ) { fprintf(stderr,"error in write, result = %d\n", result2); }
    2.) Above example is a hex command which have length of 8byte. The problem now is i have a .txt file which content below data:

    \xf5\x00\x00\x00\x17\x0a\x16\x01\x61\x0e\x1d\x1a\x 01\x16\x95\x00\xc1\x1f\x26\x44\xa1\x1f\x88\x95\xe1 \x24\x1f\xda\x41\x26\x92\x00\x01\x35\x90\x00\x81\x 43\x22\x44\x21\x53\x16\x01\x61\x57\xa2\x44\x81\x65 \x92\xab\x01\x69\x89\xa6\xa1\x79\x29\xc8\x01\x7d\x 2c\x09\x01\x7e\x8e\x22\xe1\x82\xa5\x47\x21\x84\xab \x02\x01\x8a\xac\x2b\x21\x09\x1c\x03\x82\x2b\x8f\x 97\x82\x2e\x1a\x59\xc2\x6e\x95\x40\x02\x01\x00\x00 \x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x 00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x 00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x 00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x 00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\xf5 <==(207 byte data)

    ==>In the same c file where i need to send the hex command, i called the above data and put it into an array. The problem now is i need to combine the data in that particular array with the hex command which i gave in (1) before i send it to the port. How can i do that? Below is my current code which have error:

    Code:
    /* Include files */
    #include<stdio.h>
    #include<string.h>
    #include <stdlib.h>
    //------------------------------------------------------------//
     #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 */
    //------------------------------------------------------------//
    
    #define VIN_FILE 2
    
    /* Constants to define position in file */
    #define BEGIN 0
    #define CURRENT 1 
    
    
    #define VIN_RECLEN 30
    
    FILE *fpVin;
    
    /* Function declarations */
    
    int fnOpenFile ( int iFile );
    int fnReadFile ( char acLine[], int iFile, int ipos);
    
    int fnReadFile ( char acLine[], int iFile, int ipos)
    {
        if( iFile==VIN_FILE)
        {
            if (ipos == BEGIN) {
                if (fseek(fpVin, 0L, SEEK_SET)) {
                    return 1;
                }
            }
            if ( NULL == fgets (acLine, VIN_RECLEN, fpVin) ) {
                return 1;
            }
        }
        return 0;
    }
    
    int fnOpenFile ( int iFile )
    {
        char acFileName[20];
    
    
    
       if(iFile==VIN_FILE)
       {
            strcpy (acFileName, "eigen_from_database.txt");
            fpVin = fopen(acFileName, "r+");
            if (NULL == fpVin) {
                return 1;
            }        
       }
        return 0;
    }
    
     int open_port(void)
     {
       int fd;                                   /* File descriptor for the port */
    
       fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
    
       if (fd < 0) {                                              /* Could not open the port */
         fprintf(stderr, "open_port: Unable to open /dev/ttyUSB0 - %s\n",
                 strerror(errno));
       }
    
       return (fd);
     }
    
    
    int main()
    {
            int iRet,count, int_1, int_2, mainfd=0, i;
        /*data row*/
        char acVinRecord[900], cnt, result2, temp_final[800];
        unsigned char result,chout;
        struct termios options;
            FILE *fp;
    
    //===========================Open port and set configuration=========================================//
    mainfd = open_port();
    
     fcntl(mainfd, F_SETFL, FNDELAY);                  /* Configure port reading */
                                         /* Get the current options for the port */
     tcgetattr(mainfd, &options);
     cfsetispeed(&options, B19200);                 /* Set the baud rates to 19200 */
     cfsetospeed(&options, B19200);
        
                                       /* Enable the receiver and set local mode */
     options.c_cflag |= (CLOCAL | CREAD);
     options.c_cflag &= ~PARENB; /* Mask the character size to 8 bits, no parity */
     options.c_cflag &= ~CSTOPB;
     options.c_cflag &= ~CSIZE;
     options.c_cflag |=  CS8;                              /* Select 8 data bits */
     options.c_cflag &= ~CRTSCTS;               /* Disable hardware flow control */  
     
                                     /* Enable data to be processed as raw input */
     options.c_lflag &= ~(ICANON | ECHO | ISIG);
           
                                            /* Set the new options for the port */
     
    //============Take the 207 byte data and put it in an array (temp_string)============================//
        /* Open the Policy file to start with */
        iRet = fnOpenFile (VIN_FILE);
    
        /* Unable to open file results ? */
        if (iRet != 0) {
            printf ("\t\t\tERROR: Unable to open the file results\n");
            
            exit (1);
        }
    
        /* Read the first record */
        iRet = fnReadFile (acVinRecord, VIN_FILE, BEGIN);
        count=0;
        /* Read further till the last record */
        while (0 == iRet) 
           {
        count=count+1;
        int_1 = 0; int_2= 0;
        switch (count)
        {
        case 1:
    
        for (int_1=0, int_2=0; int_1<29,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
    
        case 2:
        
        for (int_1=29, int_2=0; int_1<58,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        //temp_final[58]= '\0';
        break;
    
        case 3:
        
        for (int_1=58, int_2=0; int_1<87,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
    
        break;
    
        case 4:
        
        for (int_1=87, int_2=0; int_1<116,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
    
        case 5:
        
        for (int_1=116, int_2=0; int_1<145,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
    
        break;
        
        case 6:
        
        for (int_1=145, int_2=0; int_1<174,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        
        break;
    
        case 7:
        
        for (int_1=174, int_2=0; int_1<203,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
    
        case 8:
        
        for (int_1=203, int_2=0; int_1<232,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
    
        case 9:
        
        for (int_1=232, int_2=0; int_1<261,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
    
        case 10:
        
        for (int_1=261, int_2=0; int_1<290,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
    
        case 11:
        
        for (int_1=290, int_2=0; int_1<319,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
    
        case 12:
        
        for (int_1=319, int_2=0; int_1<348,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
    
        case 13:
        
        for (int_1=348, int_2=0; int_1<377,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
    
        case 14:
        
        for (int_1=377, int_2=0; int_1<406,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
    
        case 15:
        
        for (int_1=406, int_2=0; int_1<435,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
    
        case 16:
        
        for (int_1=435, int_2=0; int_1<464,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
    
        case 17:
        
        for (int_1=464, int_2=0; int_1<493,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
    
        break;
    
        case 18:
        
        for (int_1=493, int_2=0; int_1<522,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
    
        case 19:
        
        for (int_1=522, int_2=0; int_1<551,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
    
        case 20:
        
        for (int_1=551, int_2=0; int_1<580,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
    
        case 21:
        
        for (int_1=580, int_2=0; int_1<609,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
    
        case 22:
        
        for (int_1=609, int_2=0; int_1<638,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
        
        case 23:
        
        for (int_1=638, int_2=0; int_1<667,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
    
        case 24:
        
        for (int_1=667, int_2=0; int_1<696,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
    
        case 25:
        
        for (int_1=696, int_2=0; int_1<725,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
        
        case 26:
        
        for (int_1=725, int_2=0; int_1<754,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
    
        case 27:
        
        for (int_1=754, int_2=0; int_1<783,int_2<29;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        break;
    
        case 28:
        
        for (int_1=783, int_2=0; int_1<797,int_2<14;int_1++,int_2++)
        {
            temp_final[int_1] = acVinRecord[int_2];
        }
        temp_final[797]= '\0';
        break;
        }
    
            iRet = fnReadFile (acVinRecord, VIN_FILE, CURRENT);
        }
        
        printf("%s\n",temp_final);
    
    //====This is where i need to combine data in array with the existing hex command==========//
        tcsetattr(mainfd, TCSANOW, &options);
     
         tcflush(mainfd, TCIOFLUSH); // Clear both the input and output buffers.
    
        result2 = write(mainfd,"\xF5\x41\x00\x00\x00\x00\x41\xF5\xF5\x00\x01\x01\\%s\",211,temp_final);
         if ( result2 == -1 ) { perror("error: write"); }
         if ( result2 != 8 ) { fprintf(stderr,"error in write, result = %d\n", result2); }
        
        close(mainfd);        /* Close the serial port */
    //==========================================================================================//
        
    }
    p/s: just ignore any unuseful function in the code. abit mess.
    Last edited by Marvin Gorres; 04-18-2013 at 02:17 PM.

  10. #70
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    I really don't understand your problem. But I do see a couple of problems:

    Code:
        char acFileName[20];
     
     
     
       if(iFile==VIN_FILE)
       {
            strcpy (acFileName, "eigen_from_database.txt");
            fpVin = fopen(acFileName, "r+");
    In the above snippet you have an out of bounds array access. You allocated 20 bytes to your array, how many characters are in that string constant? Why are you even doing the strcpy()? Why not just use the string constant for the fopen parameter?


    Next: What's with all the case statements?

    Are you sure your reading your file correctly? Have you printed out the values of that array to insure it is correct?

    Please post the exact contents of your input file.


    Jim

  11. #71
    Registered User
    Join Date
    Apr 2013
    Posts
    66
    Quote Originally Posted by jimblumberg View Post
    I really don't understand your problem. But I do see a couple of problems:

    Code:
        char acFileName[20];
     
     
     
       if(iFile==VIN_FILE)
       {
            strcpy (acFileName, "eigen_from_database.txt");
            fpVin = fopen(acFileName, "r+");
    In the above snippet you have an out of bounds array access. You allocated 20 bytes to your array, how many characters are in that string constant? Why are you even doing the strcpy()? Why not just use the string constant for the fopen parameter?


    Next: What's with all the case statements?

    Are you sure your reading your file correctly? Have you printed out the values of that array to insure it is correct?

    Please post the exact contents of your input file.


    Jim
    Yes Jim, i printed out the correct data from the .txt file. The problem is how to combine the hex data in array(temp_final) with the existing hex data in here ==> "result2 = write(mainfd,"\xF5\x23\x00\x00\x00\x00\x23\xF5",8) ;"

    I've posted the content of the input file #69
    Last edited by Marvin Gorres; 04-18-2013 at 11:23 PM.

  12. #72
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    > 2.) Above example is a hex command which have length of 8byte. The problem now is i have a .txt file which content below data:
    > \xf5\x00\x00\x00\x17\x0a
    When you're writing bytes using \xnn in C code, then the compiler is responsible for stripping off the \x and interpreting nn as a single byte.

    When you're reading from a file, it's your responsibility to read 4 chars, strip off the \x and assign the conversion of "nn" to nn.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  13. #73
    Registered User
    Join Date
    Apr 2013
    Posts
    66
    Quote Originally Posted by Salem View Post
    > 2.) Above example is a hex command which have length of 8byte. The problem now is i have a .txt file which content below data:
    > \xf5\x00\x00\x00\x17\x0a
    When you're writing bytes using \xnn in C code, then the compiler is responsible for stripping off the \x and interpreting nn as a single byte.

    When you're reading from a file, it's your responsibility to read 4 chars, strip off the \x and assign the conversion of "nn" to nn.
    I can read the file exactly as it is in .txt file n it looks like this:

    \xf5\x00\x00\x00\x17\x0a\x16\x01\x61\x0e\x1d\x1a\x 01\x16\x95\x00\xc1\x1f\x26\x44\xa1\x1f\x88\x95\xe1 \x24\x1f\xda\x41\x26\x92\x00\x01\x35\x90\x00\x81\x 43\x22\x44\x21\x53\x16\x01\x61\x57\xa2\x44\x81\x65 \x92\xab\x01\x69\x89\xa6\xa1\x79\x29\xc8\x01\x7d\x 2c\x09\x01\x7e\x8e\x22\xe1\x82\xa5\x47\x21\x84\xab \x02\x01\x8a\xac\x2b\x21\x09\x1c\x03\x82\x2b\x8f\x 97\x82\x2e\x1a\x59\xc2\x6e\x95\x40\x02\x01\x00\x00 \x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x 00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x 00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x 00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x 00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\xf5 <==(207 byte data)

    So, in order to send the hex command using this:

    result2 = write(mainfd,"\xF5\x23\x00\x00\x00\x00\x23\xF5",8) ;

    The problem is how do i insert the data i read into the coding above??

  14. #74
    Registered User
    Join Date
    Apr 2013
    Posts
    66
    To make a quick understanding, i have one example:

    Code:
    char  get[1000] = {"\\xF5\\x01\\x00\\x00"}, get_2[1000] = {"\\x00\\x00\\x01\\xF5"};
    
    strcat(get,get_2);
    
        printf("%s",get);  //output is \xf5\x01\x00\x00\x00\x00\x01\xf5 (string)
    
        result2 = write(mainfd,get,8);
    The code above explained that there's a two array which both have been assigned a string content, and then combine it using strcat. After that i tried to write to the port but failed, because what i'm writing to the port is STRING, it required HEX. That's the problem. the data need to be sent is correct but it's in string format.

  15. #75
    Registered User
    Join Date
    Aug 2005
    Location
    Austria
    Posts
    1,990
    Code:
    result2 = write(mainfd,get,8);
    Your program will only send the first 8 characters of that string and this is
    \xF5\x00
    you need to find a way to convert the string \xF5\x01\x00\x00\x00\x00\x01\xF5
    into an array of unsigned characters that contains
    Code:
    unsigned char buff[] = { 245, 1, 0, 0,  0, 0, 1, 245 };
    And note. there isn't anything like hex data. There exists only hex representation of data
    Kurt
    Last edited by ZuK; 04-19-2013 at 02:33 AM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. read in from serial port; getchar()
    By s.dodd in forum C Programming
    Replies: 9
    Last Post: 10-23-2011, 10:50 AM
  2. Can't Read From Serial Port
    By HalNineThousand in forum Linux Programming
    Replies: 14
    Last Post: 03-20-2008, 05:56 PM
  3. serial port read
    By mackrackit in forum C++ Programming
    Replies: 6
    Last Post: 03-22-2006, 01:40 AM
  4. How to read from a serial port
    By WDT in forum C++ Programming
    Replies: 6
    Last Post: 05-10-2004, 06:31 AM
  5. Serial port read..can someone tell me..
    By Unregistered in forum C Programming
    Replies: 3
    Last Post: 06-27-2002, 08:21 AM

Tags for this Thread