概述
环境 win7 Anaconda2
一、安装pyserial和modbus-tk:
C:Usersadmin>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:Anaconda2Scripts
Using c:anaconda2libsite-packagespyserial-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:usersadminappdatalocaltempeasy_install-z7i_tmmodbus_tk-0.5.7setup.cfg
Running modbus_tk-0.5.7setup.py -q bdist_egg –dist-dir c:usersadminappdatalocaltempeasy_install-z7i_tmmodbu
s_tk-0.5.7egg-dist-tmp-4bmk1v
zip_safe flag not set; analyzing archive contents…
Moving modbus_tk-0.5.7-py2.7.egg to c:anaconda2libsite-packages
Adding modbus-tk 0.5.7 to easy-install.pth file
Installed c:anaconda2libsite-packagesmodbus_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客户端向服务器发送终止连接请求。
- 使用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 failedrn”);
return;
}
Printf("http server response!rn");
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中实现
}
- 使用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 tcprn”);
ret = read(conn, recv_buffer, buflen);
while ( ret > 0 )
{
ret = read(conn, recv_buffer, buflen);
Printf(“rn>:”); // debug print
for(i=0; i
最后
以上就是慈祥楼房为你收集整理的树莓派 linux下modbus总结(TCP-modbus,RS232-modbus)ifndef _SS_PAD1SIZEendifif LWIP_COMPAT_SOCKETSdefine 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 connec的全部内容,希望文章能够帮你解决树莓派 linux下modbus总结(TCP-modbus,RS232-modbus)ifndef _SS_PAD1SIZEendifif LWIP_COMPAT_SOCKETSdefine 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 connec所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复