Linux Reads Serial Data

This is a program that reads the data in the serial port in linux and stores it in the local satabase.

Posted by Dusign on 2019-03-25
Words 1.3k and Reading Time 6 Minutes
Viewed Times

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:
data format

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:

1
2
3
4
5
6
7
8
9
10
11
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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:

1
2
3
4
5
6
7
8
9
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

1
2
3
4
5
6
int executesql(const char * sql) {
/*query the database according the sql*/
if (mysql_real_query(g_conn, sql, strlen(sql)))
return -1;
return 0;
}

If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them. Thank you !

...

...

00:00
00:00