Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serial port binary transfer changes carriage return

I have been trying to implement a primitive serial file transfer protocol in C this past week and I've come across a really weird problem which I can't seem to find the solution for online. I've managed to transfer binary data over the serial port and receive it but in the process, all the "0D" bytes are converted to "0A". The following is my code.

#include <stdlib.h>
#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 <signal.h>
#include <sys/ioctl.h>
#include <termios.h>

//eventually plan to set up a proper communication protocol
#define ACK 0x01 
#define NAK 0x00

int setAttribs (int fd, int speed, int parity);
unsigned char* readFile(char* filename, int* file_size);

int main(void){

    //set up ports
    int fd = 0, r = 0, i = 0;
    fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);//sending port
    if(fd<0){
            perror("open port ttyUSB0 failed");
            return -1;
    }
    setAttribs(fd,1500000,0);

    int rd =0, file_size=0, bytes=0;
    rd = open("/dev/ttyUSB1", O_RDWR | O_NOCTTY | O_NDELAY);//receiving port
    setAttribs(rd,1500000,0);

    //create file to which the binary data will be written
    FILE *newFile;
    newFile = fopen("t.bin","wb");
    if(newFile<0){
            printf("open file failed\n");
            return -1;
    }

    //This character array will hold the file to be transferred
    unsigned char* data = '\0';
    data = readFile("t.odt", &file_size);

    ioctl(rd, TCFLSH, &bytes);//port flush which does not seem to work      
    do{

            //write data in 1024 byte chunks
            write(fd,data+i,1024);

            //wait for write to finish
            usleep(8500);

            //buffer to hold received bytes
            unsigned char buffer[1024];

            //ensure buffer is empty
            memset(buffer,0,1024);

            //read in 1024 byte chunks
            read(rd, buffer, 1024);

            //printing bytes in the buffer to check for errors
            for(r=0;r<1024;r++){
                    if(r%16==0)
                            printf("\n");
                    printf("%02X ", buffer[r]);
            }

            //write to file in 1024 byte chunks
            fwrite(buffer, 1,1024,newFile);

            //increase counter
            i+=1024;

    }while(i<8197);//its an 8088 byte file  
    printf("Done!\n");
    return 0;
}

unsigned char* readFile(char* filename, int* file_size){

    unsigned char *buffer = NULL;
    int string_size, i;
    FILE *handler = fopen(filename, "rb");

    if (handler)
    {
            // Seek the last byte of the file
            fseek(handler, 0, SEEK_END);
            // Offset from the first to the last byte, or in other words, filesize
            string_size = ftell(handler);
            printf("File length: %d\n",string_size);
            *file_size = string_size;
            // go back to the start of the file
            rewind(handler);

            // Allocate a string that can hold it all
            buffer = (unsigned char*) malloc(sizeof(unsigned char) * (string_size + 1) );

            // Read it all in one operation
            for(i=0;i<string_size;i++){
                    fread(buffer+i, sizeof(unsigned char),1, handler);
                    if(i%16==0)
                            printf("\n");
                    printf("%02X ",*(buffer+i));
            }
            // fread doesn't set it so put a \0 in the last position
            // and buffer is now officially a string
            //      buffer[string_size] = '\0';
            printf("Finished read\n");

            // Always remember to close the file
            fclose(handler);
    }
 return buffer;
}
int setAttribs (int fd, int speed, int parity)
{
    struct termios tty;
    memset (&tty, 0, sizeof tty);
    if (tcgetattr (fd, &tty) != 0)
    {
            fputs("error %d from tcgetattr", stderr);

    }
    cfsetospeed (&tty, speed);
    cfsetispeed (&tty, speed);

    tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;     // 8-bit chars
    // disable IGNBRK for mismatched speed tests; otherwise receive break
    // as \000 chars
    tty.c_iflag &= ~IGNBRK;         // disable break processing
    tty.c_lflag = 0;                // no signaling chars, no echo,
    // no canonical processing
    tty.c_oflag = 0;                // no remapping, no delays
    tty.c_cc[VMIN]  = 0;            // read doesn't block
    tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

    tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl

    tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
    // enable reading
    tty.c_cflag &= ~(PARENB | PARODD);      // shut off parity
    tty.c_cflag |= parity;
    tty.c_cflag &= ~CSTOPB;
    tty.c_cflag &= ~CRTSCTS;

    if (tcsetattr (fd, TCSANOW, &tty) != 0)
    {
            fputs("error %d from tcsetattr", stderr);

    }
    return 1;
}
                                                                                                                                                                                                                                         

All it does is write to a USB-Serial converter in one port and read through the one in the other. I was just testing it with an ODT file which is 8088 bytes (Hello World) and was trying out different baudrates and write block sizes. After some trial and error, I found that this configuration (1500000 bps and 1024 byte blocks) was both fast and relatively more reliable. The only errors are as shown below.

Transmitted bytes: 70 6E 67 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49

Received bytes: 70 6E 67 89 50 4E 47 0A 0A 1A 0A 00 00 00 0A 49

As you can see all the "0D" bytes are changed to "0A". I've tried lower baudrates and smaller block sizes and the problem persists. I understand that they are the carriage return and line feed values respectively and that line feed is used as a control value for asynchronous communication in Linux; but I don't get why that would affect the carriage return values. Is there some special significance for carriage return in serial communication? Is there a way to send "0D" byte across in that case?

TL;DR: Raw serial communication results in "0D" bytes being replaced with "0A". Any idea why? Any way to solve?

like image 550
patrick_star Avatar asked Feb 13 '17 17:02

patrick_star


People also ask

What is carriage return in serial communication?

Carriage Return & Line Feed - Carriage return and line feed are the ASCII characters sent when you press the enter key on your keyboard. These terms have roots from the days of typewriters. Carriage return meant the carriage holding the paper would return to the starting point of that particular line.

What is terminal baud rate?

ASCII charts are very helpful when working with serial terminals. Baud Rate - In short, baud rate is how fast your data is being transmitted and received. 9600 is the standard rate, but other speeds are typical amongst certain devices.


1 Answers

Thank you so much it worked!! I never knew that there was the ICRNL option which translates line feeds to carriage returns. Once I set that and others off with

tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); //tty is the name of the struct termios

it was golden.

like image 80
patrick_star Avatar answered Sep 20 '22 21:09

patrick_star