树莓派 linux下modbus总结(TCP-modbus,RS232-modbus)

环境 win7 Anaconda2

一、安装pyserial和modbus-tk:
C:\Users\admin>cd C:\Anaconda2

C:\Anaconda2>easy_install pyserial
Searching for pyserial
Best match: pyserial 3.4
Processing pyserial-3.4-py2.7.egg
pyserial 3.4 is already the active version in easy-install.pth
Installing miniterm.py script to C:\Anaconda2\Scripts

Using c:\anaconda2\lib\site-packages\pyserial-3.4-py2.7.egg
Processing dependencies for pyserial
Finished processing dependencies for pyserial

C:\Anaconda2>python
Python 2.7.12 |Anaconda 4.2.0 (64-bit)| (default, Jun 29 2016, 11:07:13) [MSC v.1500 64 bit (AMD64)] on win32
Type “help”, “copyright”, “credits” or “license” for more information.
Anaconda is brought to you by Continuum Analytics.
Please check out: http://continuum.io/thanks and https://anaconda.org

import serial

C:\Anaconda2>easy_install modbus-tk
Searching for modbus-tk
Reading https://pypi.python.org/simple/modbus-tk/
Downloading https://pypi.python.org/packages/80/71/3d8a6596cd65670d4d2beee262d5964deb933614fd7f58a739d5aa0f0332/modb
us_tk-0.5.7.tar.gz#md5=74c02b9b57dc32913da52d020eaf11d3
Best match: modbus-tk 0.5.7
Processing modbus_tk-0.5.7.tar.gz
Writing c:\users\admin\appdata\local\temp\easy_install-z7i_tm\modbus_tk-0.5.7\setup.cfg
Running modbus_tk-0.5.7\setup.py -q bdist_egg –dist-dir c:\users\admin\appdata\local\temp\easy_install-z7i_tm\modbu
s_tk-0.5.7\egg-dist-tmp-4bmk1v
zip_safe flag not set; analyzing archive contents…
Moving modbus_tk-0.5.7-py2.7.egg to c:\anaconda2\lib\site-packages
Adding modbus-tk 0.5.7 to easy-install.pth file
Installed c:\anaconda2\lib\site-packages\modbus_tk-0.5.7-py2.7.egg
Processing dependencies for modbus-tk
Finished processing dependencies for modbus-tk

C:\Anaconda2>python
Python 2.7.12 |Anaconda 4.2.0 (64-bit)| (default, Jun 29 2016, 11:07:13) [MSC v.1500 64 bit (AMD64)] on win32
Type “help”, “copyright”, “credits” or “license” for more information.
Anaconda is brought to you by Continuum Analytics.
Please check out: http://continuum.io/thanks and https://anaconda.org

import modbus_tk

二、
lwip-win32-msvc-0.1
已经解决的问题:
一:下载lwip的源码,在windows上重新组织文件架构,然后进行编译。刚好美国已经有人做了这样面的工作,所以

就先用一下咯。毕竟一开始要做这方面的工作,还是有难度的。可以下载lwip-win32-msvc-0.1.zip文件。

网络上有很多这样的下载连接,但是都不好用(动不动就要你去注册,花了时间注册好了本以为可以下载了,没想到下载时候提示,积分不够,需要积分可以啊,买卡充值什么的,烦哪。)。官方网也连接不上,后来发现在sina爱问里面的共享里面可以下载,很方便的哦。

下载完了之后解压就可以用了。用vc6打开就可以了。里面有两个工程,一个是Lwip4工程,编译后是lib库,供上层应用程序使用;另一个工程是test,是测试程序。分别编译,lwip4可以顺利的编译通过,但是test工程编译的时候会遇到问题:

(1)找不到packet32.h文件。解决的办法是到官网http://www.winpcap.org/devel.htm上下载winpcap的开发包Download WinPcap 4.0.2 Developer’s Pack,连接是http://www.winpcap.org/install/bin/WpdPack_4_0_2.zip 。下载之后解压即能看到一系列的目录,从include目录下就能找到packet32.h,以及packet32.h文件里include的devioctl.h,一起拷贝过来到工程目录里面。另外还要到Lib文件夹中拷贝packet32.h的实现lib文件packet.lib,然后加到工程中来。问题就迎刃而解了。

(2)typedef struct npf_if_addr {
struct sockaddr_storage IPAddress; ///< IP address.
struct sockaddr_storage SubnetMask; ///< Netmask for that address.
struct sockaddr_storage Broadcast; ///< Broadcast address.
}npf_if_addr;

会报packet32.h中上述结构体中的sockaddr_storage未定义:error C2079: ‘IPAddress’ uses undefined struct ‘sockaddr_storage

解决这个问题,可以自己在packet32.h中定义该结构体:

ifndef _SS_PAD1SIZE

struct sockaddr_storage {
u_char sa_len;
u_char sa_family;
u_char padding[128];
};

endif

或者sockaddr_storage 改成 sockaddr 也可以解决问题。

(3)pktif.c(191) : error C2065: ‘OID_802_3_PERMANENT_ADDRESS’ : undeclared identifier

pktif.c(199) : error C2065: ‘NDIS_PACKET_TYPE_ALL_LOCAL’ : undeclared identifier

报上述两个错误,是因为这两个变量是在Ntddndis.h文件中定义的,该头文件也是winpcap开发包中的文件,所以如同(1)需要从开发包inlude目录下把该文件拷贝到工程里来,同时在pktif.c文件头加上#include

if LWIP_COMPAT_SOCKETS

define accept(a,b,c) lwip_accept(a,b,c)

define bind(a,b,c) lwip_bind(a,b,c)

define shutdown(a,b) lwip_shutdown(a,b)

define closesocket(s) lwip_close(s)

define connect(a,b,c) lwip_connect(a,b,c)

define getsockname(a,b,c) lwip_getsockname(a,b,c)

define getpeername(a,b,c) lwip_getpeername(a,b,c)

define setsockopt(a,b,c,d,e) lwip_setsockopt(a,b,c,d,e)

define getsockopt(a,b,c,d,e) lwip_getsockopt(a,b,c,d,e)

define listen(a,b) lwip_listen(a,b)

define recv(a,b,c,d) lwip_recv(a,b,c,d)

define recvfrom(a,b,c,d,e,f) lwip_recvfrom(a,b,c,d,e,f)

define send(a,b,c,d) lwip_send(a,b,c,d)

define sendto(a,b,c,d,e,f) lwip_sendto(a,b,c,d,e,f)

define socket(a,b,c) lwip_socket(a,b,c)

define select(a,b,c,d,e) lwip_select(a,b,c,d,e)

define ioctlsocket(a,b,c) lwip_ioctl(a,b,c)

if LWIP_POSIX_SOCKETS_IO_NAMES

define read(a,b,c) lwip_read(a,b,c)

define write(a,b,c) lwip_write(a,b,c)

define close(s) lwip_close(s)

define fcntl(a,b,c) lwip_fcntl(a,b,c)

endif /* LWIP_POSIX_SOCKETS_IO_NAMES */

endif /* LWIP_COMPAT_SOCKETS */

int socket(int domain, int type, int protocol);
服务器根据地址类型(ipv4,ipv6)、socket类型、协议创建socket。
domain:协议族,常用的有AF_INET、AF_INET6、AF_LOCAL、AF_ROUTE其中AF_INET代表使用ipv4地址
type:socket类型,常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等
protocol:协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
把一个地址族中的特定地址赋给socket
sockfd:socket描述字,也就是socket引用
addr:要绑定给sockfd的协议地址
addrlen:地址的长度
通常服务器在启动的时候都会绑定一个地址(如ip地址+端口号),用于提供服务。有些端口号是约定俗成的不能乱用,如80用作http,502用作modbus。

int listen(int sockfd, int backlog);
监听socket
sockfd:要监听的socket描述字
backlog:相应socket可以排队的最大连接个数

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
连接某个socket
sockfd:客户端的socket描述字
addr:服务器的socket地址
addrlen:socket地址的长度

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
TCP服务器监听到客户端请求之后,调用accept()函数取接收请求
sockfd:服务器的socket描述字
addr:客户端的socket地址
addrlen:socket地址的长度

size_t read(int fd, void *buf, size_t count);
读取socket内容
fd:socket描述字
buf:缓冲区
count:缓冲区长度

size_t write(int fd, const void *buf, size_t count);
向socket写入内容,其实就是发送内容
fd:socket描述字
buf:缓冲区
count:缓冲区长度

int close(int fd);
socket标记为以关闭 ,使相应socket描述字的引用计数-1,当引用计数为0的时候,触发TCP客户端向服务器发送终止连接请求。

  1. 使用socket创建嵌入式WebServer
     要使用socket的前提是已经做好lwip和rtos的移植,如果低层驱动移植完毕,就可以使用socket快速创建应用。
    本例是一个简单的WebServer。

const unsigned char htmldata[] = “\
\
LWIP\

A WebServer Based on LwIP v1.4.1 Hello world!

\
“;
const unsigned char errhtml[] = “\
\
\
Error!\
\
\

404 - Page not found

\
\
“;

/**
* @brief serve tcp connection
* @param conn: connection socket
* @retval None
*/
void http_server(int conn)
{
int buflen = 1500;
int ret;
unsigned char recv_buffer[1500];

/* Read in the request */
ret = read(conn, recv_buffer, buflen);
if(ret <= 0)
{
close(conn);
Printf(“read failed\r\n”);
return;
}

Printf("http server response!\r\n");
if(strncmp((char *)recv_buffer, "GET /lwip", 9) == 0)
{
    write(conn, htmldata, sizeof(htmldata)-1);
}
else
{
    write(conn, errhtml, sizeof(errhtml)-1);
}
/* Close connection socket */
close(conn);

}

/**
* @brief http_task
* @param arg: pointer on argument(not used here)
* @retval None
*/
static void http_task(void *arg)
{
int sock, newconn, size;
struct sockaddr_in address, remotehost;

/* create a TCP socket */
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
Printf(“can not create socket”);
return;
}

/* bind to port 80 at any interface */
address.sin_family = AF_INET;
address.sin_port = htons(80);
address.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, (struct sockaddr *)&address, sizeof (address)) < 0)
{
Printf(“can not bind socket”);
close(sock);
return;
}

/* listen for connections (TCP listen backlog = 1) */
listen(sock, 1);
size = sizeof(remotehost);
while (1)
{
newconn = accept(sock, (struct sockaddr )&remotehost, (socklen_t )&size);
if (newconn >= 0)
{
http_server(newconn);
}
else
{
close(newconn);
}
}
}

/****************************************************
* void http_task_init(void)
*
* This function initializes the service.
****************************************************/
void http_task_init(void)
{
sys_thread_new( CHARGEN_THREAD_NAME, http_task, 0, 0, TCPIP_THREAD_PRIO+1); //函数栈在移植sys_thread_new中实现
}

  1. 使用socket创建Modbus TCP应用
    Modbus TCP在网络传输层次,就是一串有特定含义的数据包的交互,LwIP层次并不识别是什么数据。所以从这个角度来讲,Modbus TCP移植和其他TCP应用的移植没有任何差别。
    透过表面看本质,只有拨开外层重重包装看本质,我们才能从纷杂的事件中找到问题的重点,然后剥离不相关的部分,一次解决一个问题。基于这个思想,Modbus TCP应用可以直接划分为2个层次,底层是驱动部分,负责一包数据从网络上接收上来或发送出去,上层是Modbus的协议部分,就是Modbus寄存器的操作等。从这个角度来说,只要数据传输正确了,那么怎么处理就是另一个问题了,比如可以共用Modbus RS485的代码等。
    下面测试了Modbus TCP的数据传输。
    Modbus TCP设计有几个重要的点:
      1)Modbus是连续通信,不能和http一样完成一次连接后就断开,所以要不停的read,当读出错时在close(conn)关闭连接。
      2)Modbus可能存在通信失败情况,需要关闭socket后再重新建立socket。
      3)Modbus作为工业协议,应用场景下一般不会多个客户端连接一台机器,并且多个客户端连接一台机器,寄存器的读写互斥会是一个大问题,所以常见的做法是一旦连接成功,就关闭socket禁止其他连接进来。客户端主动断开后再重新建立socket然后进入listen状态。

/**
* @brief serve modbus_tcp connection
* @param conn: connection socket
* @retval None
*/
void modbus_tcp_server(int conn)
{
int buflen = 1500;
int ret;
unsigned char recv_buffer[1500];
int i;

Printf(“start modbus tcp\r\n”);
ret = read(conn, recv_buffer, buflen);
while ( ret > 0 )
{
ret = read(conn, recv_buffer, buflen);
Printf(“\r\n>:”); // debug print
for(i=0; i

你可能感兴趣的:(机器视觉项目,嵌入式开发)