基于ARM开发板的简单串口服务器

时间:2021-09-14 18:08:27

串口服务器是为RS-232/485/422串口到TCP/IP网络之间完成数据转换的通讯接口转换器。提供RS-232/485/422终端串口与TCP/IP网络的数据双向透明传输,提供串口转网络功能,RS-232/485/422转网络的解决方案,可以让串口设备立即联接网络。

MOXA工业级串口服务器

基于ARM开发板的简单串口服务器

应用领域

门禁系统、考勤系统、售饭系统、 POS 系统、楼宇自控系统、自助银行系统电信机房监控,电力监控等。

产品特点

   ·体积小,火柴盒大小 
  ·支持RS232,RS485
  ·10/100M以太网口 ,软件可升级
  ·一个开关量输入,一个开关量输出
  ·可作为TCP Server 或TCP Client ,UDP数据
  ·内置WEB服务器,支持Java
  ·支持多种异步串口格式
  ·无需修改原有应用软件就可在网络环境下使用
  ·将COM口定向至IP地址,每台计算机可同时拥有256个串口

工作方式

服务器方式:在该工作方式下,串口联网服务器作为TCP服务器端, 转换器在指定的TCP端口上监听平台程序的连接请求,该方式比较适合于一个转换器与多个平台程序建立连接(一个转换器不能同时与多个平台程序建立连接)。

客户端方式:在该工作方式下,串口联网服务器 作为 TCP 客户端,转换器上电时主动向平台程序请求连接,该方式比较适合于多个转换器同时向一个平台程序建立连接。

通讯模式

点对点通讯模式:该模式下,转换器成对的使用,一个作为服务器端,一个作为客户端,两者之间建立连接,实现数据的双向透明传输。该模式适用于将两个串口设备之间的总线连接改造为 TCP/IP 网络连接。
使用虚拟串口通讯模式:该模式下,一个或者多个转换器与一台电脑建立连接,实现数据的双向透明传输。由电脑上的虚拟串口软件管理下面的转换器,可以实现一个虚拟串口对应多个转换器, N 个虚拟串口对应 M 个转换器( N<=M )。该模式适用于串口设备由电脑控制的 485 总线或者 232 设备连接。
基于网络通讯模式: 该模式下,电脑上的应用程序基于SOCKET 协议编写了通讯程序,在转换器设置上直接选择支持 SOCKET 协议即可。

实现原理

基于ARM开发板的简单串口服务器

ARM开发板
一般的开发板都可使用,只要带有网口(有线/无线)、串口。

基于ARM开发板的简单串口服务器

开发板串口服务端源码

#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <termios.h> /*PPSIX终端控制定义*/
#include <errno.h> /*错误号定义*/


#define COM_FILE "/dev/ttySAC0" //串口设备文件
#define PORT 8848
#define BACKLOG 5
#define MAXDATASIZE 128
#define BUFSIZE 512



void* net_proc(void* arg);//网络侦听服务

void* net_recv(void* arg);//接收网络客户端数据

void* net_send(void* arg);//发送数据到网络客户端数据

void* com_recv(void* arg);//串口接收处理

void* com_send(void* arg);//串口发送处理

int init_com(void);//初始化串口参数



struct ARG {
int connfd;
struct sockaddr_in client;
};



static char net_buff[BUFSIZE];
static char com_buff[BUFSIZE];
int wr_index = 0;
int re_index = 0;

/**********************************
主函数
**********************************/

int main(void)
{

int fd = 0;
pthread_t net_th, com_r,com_s;

memset(net_buff,0,BUFSIZE);
memset(com_buff,0,BUFSIZE);


if((fd = init_com())<=0){
perror("init_com() error");
exit(1);
}

//网络服务器线程

if(pthread_create(&net_th, NULL, net_proc, NULL)){
perror("pthread_creat() error");
exit(1);
}

//串口接收线程

if(pthread_create(&com_r, NULL, com_recv, (void*)&fd)){
perror("pthread_creat() error");
exit(1);
}

//串口发送线程

if(pthread_create(&com_s, NULL, com_send, (void*)&fd)){
perror("pthread_creat() error");
exit(1);
}

pthread_join(net_th,NULL);
pthread_join(com_r,NULL);
pthread_join(com_s,NULL);

}


/**********************************
网络服务端线程
**********************************/


void* net_proc(void* arg)

{

int listenfd, connectfd;

pthread_t thread; //id of thread

struct ARG *acc_arg;

struct sockaddr_in server; //server's address info

struct sockaddr_in client; //client's

int sin_size;

// int opt = SO_REUSEADDR;



//create tcp socket

printf("socket.... ");

if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){

perror("creating socket failed.");

exit(1);

}



// setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

bzero(&server,sizeof(server));

server.sin_family = AF_INET;

server.sin_port = htons(PORT);

server.sin_addr.s_addr = htonl(INADDR_ANY);

printf("bind.... ");



if(bind(listenfd,(struct sockaddr *)&server,sizeof(struct sockaddr)) == -1){

perror("bind error.");

exit(1);

}



printf("listen.... ");

if(listen(listenfd,BACKLOG) == -1) {

perror("listen() error ");

exit(1);

}

sin_size = sizeof(struct sockaddr_in);

sleep(1);



while(1){

printf("accepting....\n ");

if((connectfd = accept(listenfd,(struct sockaddr *)&client,(socklen_t*)&sin_size)) == -1) {

perror("accept() error ");

exit(1);

}

acc_arg = malloc(sizeof(struct ARG));

acc_arg->connfd = connectfd;

memcpy((void *)&acc_arg->client, &client, sizeof(client));
// 服务端接收客户端数据
if(pthread_create(&thread, NULL, net_recv, (void*)acc_arg)){
perror("pthread_creat() error");
exit(1);
}

// 服务端向客户端发送数据
if(pthread_create(&thread, NULL, net_send, (void*)acc_arg)){
perror("pthread_creat() error");
exit(1);
}
}

close(listenfd);

return (0);

}

/**********************************
网络数据接收线程
**********************************/


void* net_recv(void* arg)

{

int connectfd;

int nbyte;

char recvbuf[MAXDATASIZE];

struct sockaddr_in client;
struct ARG * n_arg = arg;
connectfd = n_arg->connfd;
client = n_arg->client;



printf("IP: %s. \n",inet_ntoa(client.sin_addr) );

//get client's name from client

while(1){

memset(recvbuf,0,MAXDATASIZE);

nbyte = recv(connectfd, recvbuf, MAXDATASIZE, 0);

if(nbyte== 0 || nbyte == -1) {

close(connectfd);

perror("Client disconnected. ");

break;

}

recvbuf[nbyte] = '\0';

printf("fd=%d nbyte=%d net recv: %s \n", connectfd,nbyte,recvbuf );

// send(connectfd, recvbuf, strlen(recvbuf), 0); //回传到客户端

//网络收到的数据,存到全局缓冲区

strcat(net_buff,recvbuf);

}

}



/**********************************
网络数据发送线程
**********************************/


void* net_send(void* arg)

{

int connectfd;

int nbyte;

struct ARG * n_arg = arg;

connectfd = n_arg->connfd;



while(1){
//发送串口收到的数据到网络客户端

if(strlen(com_buff)>0)
{
nbyte = send(connectfd, com_buff, strlen(com_buff), 0);
printf("net send nbyte=%d\n",nbyte);
memset(com_buff, '\0', BUFSIZE);
}

}

}


/**********************************
串口初始化
**********************************/


int init_com(void)
{

int fd_com;

struct termios initial_settings, new_settings;

fd_com = open(COM_FILE, O_RDWR | O_NOCTTY );

if(-1 == fd_com)
{
perror("Can't Open Serial Port");
return -1;
}


//获取终端属性

tcgetattr(fd_com,&initial_settings);
new_settings = initial_settings;

//设置速度

cfsetispeed(&new_settings, B115200);//波特率115200
cfsetospeed(&new_settings, B115200);

/*控制模式设置*/

// new_settings.c_cflag &= ~CSIZE; //数据位8

new_settings.c_cflag |= CS8; //数据位8

new_settings.c_cflag &= ~CSTOPB; //停止位1

new_settings.c_cflag &= ~PARENB; //无校验

/*输入模式设置*/
new_settings.c_iflag &= ~INPCK; //取消校验

new_settings.c_iflag |= IGNPAR; //忽略奇偶校验错误的字符

new_settings.c_iflag |= ICRNL; //收到的回车符转成换行符

/*输出模式设置*/

new_settings.c_oflag = 0;

/*本地模式设置*/
new_settings.c_lflag &= ~ICANON; //启用非标准输入
// new_settings.c_lflag &= ~ECHO; //关闭回显功能

new_settings.c_lflag &= ~ISIG; //禁用信号

/*控制字符设置*/
new_settings.c_cc[VMIN] = 1; //有字符立即读取
new_settings.c_cc[VTIME] = 0;

//启用新的终端设置
if(tcsetattr(fd_com, TCSANOW, &new_settings) != 0) {
fprintf(stderr,"could not set attributes\n");
}

// tcsetattr(fd_com,TCSANOW,&initial_settings); //恢复原有设置
return fd_com;

}

/**********************************
串口数据发送
**********************************/


void* com_send(void* arg)
{
int fd;
int nbyte;
fd = *(int*)arg;

while(1)
{
if(strlen(net_buff)>0)
{
nbyte = write(fd,net_buff,strlen(net_buff));
printf("com send nbyte=%d\n",nbyte);
memset(net_buff, '\0', sizeof(net_buff));
}
}

}


/**********************************
串口数据接收
**********************************/


void* com_recv(void* arg)

{

int fd;

int nbyte = 0;

fd = *(int*)arg;

char recvbuf[MAXDATASIZE];

while(1)
{

//读取串口数据到缓冲区

memset(recvbuf,'\0',MAXDATASIZE);

nbyte = 0;
nbyte = read(fd,recvbuf,MAXDATASIZE);

recvbuf[nbyte] = '\0';

printf("nbyte=%d com recv:%s\n",nbyte,recvbuf);

strcat(com_buff,recvbuf);
}

}

PC客户端源程序

/* cthread.c */

#include <stdio.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <pthread.h>

#define PORT 8848
#define MAXDATASIZE 128

void* net_recv(void* arg);
void* net_send(void* arg);
int get_line(char *msg);
int init_net(int argc, char *argv[]);

/**********************************

主函数

**********************************/


int main(int argc, char *argv[])
{

int net_fd;
pthread_t recv_th,send_th;

net_fd = init_net(argc,argv);

pthread_create(&send_th,NULL,net_send,(void*)&net_fd);
pthread_create(&recv_th,NULL,net_recv,(void*)&net_fd);
pthread_join(recv_th,NULL);
pthread_join(send_th,NULL);

return 0;
}


/**********************************

网络接收线程

**********************************/


void* net_recv(void* arg)
{

char recvline[MAXDATASIZE];
int *sockfd;
int numbytes;
sockfd = (int*)arg;

while(1){
printf("@\n");

if((numbytes = recv(*sockfd, recvline, MAXDATASIZE, 0)) == 0){
printf("serial not data. \n");
}

//recevie message form server echo
recvline[numbytes] = '\0';

printf("[com data] %s\n", recvline);
//clean output buffer
memset(recvline, '0', strlen(recvline)-1);
}

}


/**********************************

网络初始化,连接到服务端

**********************************/


int init_net(int argc, char *argv[])
{
int fd;
struct hostent *he;
struct sockaddr_in server; //server's address info
char *defaultIP = "127.0.0.1";


if(argc != 2) {
if((he = gethostbyname(defaultIP)) == NULL){
perror("gethostbyname() error");
exit(1);
}
}else{
if((he = gethostbyname(argv[1])) == NULL){
perror("gethostbyname() error");
exit(1);
}
}

if((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
perror("socket() error");
exit(1);

}

bzero(&server , sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr = *((struct in_addr *)he->h_addr);

if(connect(fd, (struct sockaddr *)&server,sizeof(struct sockaddr)) == -1){
perror("connect() error");
exit(1);
}

return fd;

}


/**********************************

网络发送数据线程

**********************************/

void* net_send(void* arg)
{

char sendline[MAXDATASIZE],recvline[MAXDATASIZE];
int numbytes;
int len;
int * sock_fd;

sock_fd = (int*)arg;
printf("connected to server. \n");

//send message to server
//when the string is not NULL , send another!

while(1){
while((len =get_line(sendline)) != 0){
sendline[len] = '\0';
send(*sock_fd, sendline, strlen(sendline), 0);
}
}

}


/**********************************

接收命令行输入的数据

**********************************/


int get_line(char *msg)
{
int i=0;
char temp;

printf("Input message:");
fflush(stdout);

while (1) {
temp = getchar();
if (temp == '\r' || temp == '\n') {return i ;}
msg[i]=temp;
if(msg[i]==13){
msg[i]=0;
break;
}
fflush(stdout);
i++;
}
}