Linux Reads Serial Data

Linux Reads Serial Data

Computer serial port data reading is often used when using sensors. Next, I introduce the method of reading Linux serial port data in a project. I hope it will be helpful to you.

Data Format

A set of data transmitted by the sensor through serial port is 13 bytes. The data format is as follows:

在這裏插入圖片描述

Initialize Serial Port

We need to initialize the serial port before reading the serial port data. The initialization of the serial port needs three steps:opening the serial port, setting the parameters of the serial port (including baud rate, data bit, stop bit, check bit).

Opening the Port

The procedure of opening serial port is as follow, function returns a handle:

int openPort()
{
    int fd;
    fd = open(SERIAL_PORT, O_RDWR | O_NOCTTY |O_NDELAY);   
    if(fd == -1)
  	{
        perror("open serial failed!\n");
        exit(1);
    }    
    return fd;
}

SERIAL_PORT is a serial address;
O_RDWR means to open in a readable and writable manner;
O_NOCTTY indicates that if the open file is a terminal device, the terminal will not be regarded as a process control terminal;
O_NDELAY denotes opening a file in an uninterruptible manner.

Setting the Parameters

Here is the program for setting baud rate and data bits

int setSpeed(int fd, int speed, struct termios Opt)
{
  int i;

  if(tcgetattr(STDIN_FILENO, &Opt) != 0)
  {
    perror("tcgetattr fd\n");
    return 1;
  }
  //識別波特率,設置輸入輸出波特率
  for(i = 0; i < sizeof(speed_arr) / sizeof(int); i++)
  {
    if(speed == name_arr[i])
    {
      tcflush(fd, TCIOFLUSH);
      //設置波特率
      cfsetispeed(&Opt, speed_arr[i]);
      cfsetospeed(&Opt, speed_arr[i]);
            
      //設置數據接收方式
      if(tcsetattr(fd, TCSANOW, &Opt) != 0)
      {
        perror("tcsetattr fd");
        return 1;
      }
      tcflush(fd, TCIOFLUSH);
    }
  }
  return 0;
}

int setParity(int fd, int databits, int stopbits, int parity, struct termios Opt)
{
  if(tcgetattr(fd, &Opt) != 0)
  {
    perror("tcgetattr fd");
    return 1;
  }
  Opt.c_cflag |= (CLOCAL | CREAD);       	//CLOCAL:忽略 modem 控制線。 
                                            //CREAD:	打開接受者。
  switch(databits)       				    //設置數據位數
  {
    case 7:
      Opt.c_cflag &= ~CSIZE;			//屏蔽字符大小位
      Opt.c_cflag |= CS7;			    //選擇7位數據位
      break;
    case 8:
      Opt.c_cflag &= ~CSIZE;
      Opt.c_cflag |= CS8;
      break;
    default:
      fprintf(stderr, "Unsupported data size.\n");
      return 1;
  }

  switch(parity)            //設置校驗位
  {
    case 'n':
      Opt.c_cflag &= ~PARENB;        			//清除校驗位
      Opt.c_iflag &= ~INPCK;        			//啓用輸入奇偶檢測。  
      break;
    case 'o':
      Opt.c_cflag |= PARENB;       		  	//使能校驗位
      Opt.c_cflag |= PARODD;        			//奇校驗
      Opt.c_iflag |= INPCK;            		//啓用輸入奇偶檢測。
      break;
    case 'e':
      Opt.c_cflag |= PARENB;       		  	//使能校驗位
      Opt.c_cflag &= ~PARODD;        			//偶校驗
      Opt.c_iflag |= INPCK;            		//啓用輸入奇偶檢測。
      break;
    case 's':
      Opt.c_cflag &= ~PARENB;        			//清除校驗位
      Opt.c_cflag &= ~CSTOPB;       			//設置一個停止位
      Opt.c_iflag |= INPCK;            		//啓用輸入奇偶檢測。
      break;
    default:
      fprintf(stderr, "Unsupported parity.\n");
      return 1;    
  }

  switch(stopbits)        //設置停止位
  {
    case 1:
      Opt.c_cflag &= ~CSTOPB;
      break;
    case 2:
      Opt.c_cflag |= CSTOPB;
      break;
    default:
      fprintf(stderr, "Unsupported stopbits.\n");
      return 1;
  }

  Opt.c_cflag |= (CLOCAL | CREAD);

  Opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);	
 
  Opt.c_oflag &= ~OPOST;	          //OPOST :啓用具體實現自行定義的輸出處理。
  Opt.c_oflag &= ~(ONLCR | OCRNL);        //OCRNL :將輸出中的回車映射爲新行符  
 	                                  //ONLCR :(XSI) 將輸出中的新行符映射爲回車-換行。
  Opt.c_iflag &= ~(ICRNL | INLCR);        //ICRNL :將輸入中的回車翻譯爲新行 (除非設置了 IGNCR)(否則當輸入信號有 CR 時不會終止輸入)。 
                                          //INLCR :將輸入中的 NL 翻譯爲 CR。(將收到的換行符號轉換爲Return)
  Opt.c_iflag &= ~(IXON | IXOFF | IXANY); //IXON 	:啓用輸出的 XON/XOFF 流控制。
                                          //IXOFF :啓用輸入的 XON/XOFF 流控制。
                                          //IXANY :(不屬於 POSIX.1;XSI) 允許任何字符來重新開始輸出。
  tcflush(fd, TCIFLUSH);		  //清空輸入緩存
                                          //MIN = 0 , TIME =0; 有READ立即回傳否則傳回 0,不讀取任何字元
  Opt.c_cc[VTIME] = 0;        			
  Opt.c_cc[VMIN] = 0;        					
    
  if(tcsetattr(fd, TCSANOW, &Opt) != 0)	  //設置數據接收方式
  {
    perror("tcsetattr fd");
    return 1;
  }

  return 0;
}

Initialize Port

Use the following program to initialize the serial port, the function returns a handle.

int init(void)
{
  int fd;
  struct termios opt;

  //打開串口
  fd = openPort();
  
  //設置波特率
  if(setSpeed(fd, PORT_SPEED, opt) == 1)
  {	
    printf("setSpeed failed!\n");
  	exit(1);
  }
  
  //設置數據位、停止位和校驗位
  if(setParity(fd, DATABITS, STOPBITS, PARITY, opt) == 1)
  {
  	printf("setParity failed!\n");
  	exit(1);
  } 
  if(tcsetattr(fd, TCSANOW, &opt) != 0)	//TCSANOW:不等數據傳輸完畢就立即改變屬性。
  {				//TCSADRAIN:等待所有數據傳輸結束才改變屬性。
    perror("serial error");
    return -1;
  }
  return fd;
}

Data Reading and Analysis

A set of data has 13 characters, each time we read a character, we can judge whether it is the starting bit at each reading, and separate each group of data. The judgement method is as follows:

void readPort(int fd)
{
  time_t t;
  int signal=0,pm1_0,pm1_1,pm2_0,pm2_1,pm10_0,pm10_1,pm1,pm2,pm10,temporary;
  int i,len,n,parameter1,parameter2,Initiator;
  char read_buf[24],sql_insert[200];
  while(1)
  {
    bzero(read_buf, sizeof(read_buf));    
    while((n = read(fd, read_buf, sizeof(read_buf))) > 0)
    {
      //printf("\nlen= %d \n",n);
      for(i=0;i<n;i++){
        int tst=(int)read_buf[i];

        // 判斷起始符號
        parameter2=parameter1;
        parameter1=tst;
        Initiator=parameter1+parameter1;
        // 累加標誌
        signal++;
        // 判斷起始位位置
        if(parameter2==66 && parameter1==77) signal=2;                 
        if(signal==11) pm1_0=tst;
        if(signal==12) pm1_1=tst;
        if(signal==13) pm2_0=tst;
        if(signal==14) pm2_1=tst;
        if(signal==15) pm10_0=tst;
        if(signal==16) pm10_1=tst;
        pm1=pm1_0*256+pm1_1;
        pm2=pm2_0*256+pm2_1;
        pm10=pm10_0*256+pm10_1;
        if(signal==49) signal=1;
        printf("\n pm1.0=%d   pm2.5=%d   pm10=%d \n" ,pm1,pm2,pm10);
        temporary=24;
        t=time(0);
        sprintf(sql_insert,"insert into dust_data(`date`,`pm1.0`,`pm2.5`,`pm10`,`temporary`,`host_ip`) values ('%ld','%d','%d','%d','%d','127.0.0.1')",t,pm1,pm2,pm10,temporary);
        executesql("delete from dust_data limit 1");
        executesql(sql_insert);
      }
    }
  }
}

Database Operation

I built MySQL database locally and wrote the readed datas directly into the database.

Initialize Connection

First, initialize the database using the following method:

int init_mysql() {
    // init the database connection
    g_conn = mysql_init(NULL);
    
    /* connect the database */
    if(!mysql_real_connect(g_conn, g_host_name, g_user_name, g_password, g_db_name, g_db_port, NULL, 0))
        return -1;
    return 0; 
}

g_host_name is the database address;
g_user_name is the database user name;
g_password is the database password;
g_db_name is the name of the database;
g_db_port is the database port;

Database Operation

int executesql(const char * sql) {
    /*query the database according the sql*/
    if (mysql_real_query(g_conn, sql, strlen(sql)))
        return -1; 
    return 0; 
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章