西门子S7通讯——基于Snap7

0.环境配置

0.0 下载 Snap源码:

官网地址:https://snap7.sourceforge.net/
这里我下载的 1.4.2/snap7-full-1.4.2.7z 。

0.1 库编译与安装

这里我使用的是 x86 架构下的 Unbuntu 64 位系统,如果使用其他系统,请根据实际情况调整。

  • 解压并进入源码文件目录下,这里解压出来的文件名称为:snap7-full-1.4.2
  • 编译并安装sanp7库
sudo make -f x86_64_linux.mk clean
sudo make -f x86_64_linux.mk all
sudo make -f x86_64_linux.mk install
  • 将 .h 、.cpp 和库文件拷贝到程序依赖文件目录
    • 1.3.1 库文件路径位于:./snap7-full-1.4.2/build/bin/x86_64-linux/libsnap7.so
    • 1.3.2 .h 与 .cpp 路径: ./snap7-full-1.4.2/examples/cpp/snap7.h 和 ./snap7-full-1.4.2/examples/cpp/snap7.cpp

1.连接PLC

此处需要在博图软件中,查询到PLC所配置的网络的地址,并将电脑配置到同一网段。此外还需要获知所需读写的PLC的机架号与槽号,连接时需要用到。

/* RemAddress: PLC IP 地址
 * Rack: PLC 机架号
 * slot: PLC 槽号
 */
int ConnectTo(const char *RemAddress, int Rack, int Slot);
// 示例
TS7Client *client_ = new TS7Client();
int result = 0;
if((result = client_->ConnectTo(PLC_IP, PLC_RACK_NUMBER, PLC_SLOT_NUMBER) != 0)) {
    std::cerr << "Connect PLC Error ! err msg : "
    		  << CliErrorText(result).data() << std::endl;
}

2. 读写操作

2.1 使用DBRead读写
  • 读取一个bool值
/*	DBNumber:DB 区编号
 *	Start:起始地址
 *	Size:读取的字节数,最少读取一个字节。
 *	*pUsrData 存储数据的变量指针。
 */
int DBRead(int DBNumber, int Start, int Size, void *pUsrData);
// 在读取完成后,需要手动进行位运算,其中低位为x.0。
bool value = false;
unsigned char data;
client_->DBRead(66, 0, 1, &data);
value = ((data >> 3) & 1);
  • 写一个bool值
/*	DBNumber:DB 区编号
 - Start:起始地址
 - Size:写入的字节数,最少写入一个字节。
 - *pUsrData 存储数据的变量指针。
 */
int DBWrite(int DBNumber, int Start, int Size, void *pUsrData)
// 在读取完成后,需要手动进行位运算,其中低位为x.0。
bool value = false;
unsigned char data;
data = value > 0 ? (data | (value << 3)) : (data & (~(value << 3)));
client_->DBWrite(66, 0, 1, &data);

此外,还有MBRead、MBWrite、EBRead、EBWrite等等,使用方法上除了无需传入DBNumber外相同。

2.2 使用 ReadArea 与 WriteArea 进行读写操作

相较于 DBRead 和 DBWrite 等函数接口,使用 ReadArea 和 WriteArea 的可操作行更高。从源码中看,实际上 DBRead 和 DBWrite 等函数接口也均是基于 ReadArea 和 WriteArea 进行封装的。

  • ReadArea 数据块读取
/** Area : 数据块类型,需根据snap7.h中定义的 Area ID 输入
 - DBNumber:DB 区编号
 - Start:起始地址
 - Amount:需要读取的 WordLen 数量
 - WordLen:pUsrData 中的字节长度,需要使用根据snap7.h中定义的 Word Length 输入。
 - *pUsrData 存储数据的变量指针
 **/
int WriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData)
// 示例
unsigned char data[sizeof(float)];
float read_real = 0.0f ;
int result = 0;

if((result = client_->ReadArea(S7AreaDB, 66, 0x400, sizeof(float), S7WLByte, &data) != 0){
	std::cerr << "Read PLC area fail, error code : " << CliErrorText(result).data() << std::endl;
}

// 数据端序转换 !!!
for (int i = 0; i < sizeof(float); i++) {
	*((unsigned char*)read_real + i) = data[sizeof(float) - i - 1];
}

std::count << "Read real : " << read_real << std::endl;
  • WriteArea 数据块写入
unsigned char data[sizeof(float)];
float read_real = 666.66f ;
int result = 0;

for (int i = 0; i < sizeof(float); i++) {
	data[sizeof(float) - 1 - i] = *((unsigned char*)read_real + i);
}

if((result = client_->WriteArea(S7AreaDB, 66, 0x400, sizeof(float), S7WLByte, &data) != 0){
	std::cerr << "Read PLC area fail, error code : " << CliErrorText(result).data() << std::endl;
}

std::count << "Read real : " << read_real << std::endl;

注意:
1、当 WordLen 类型为 S7WLBit 时,Amount只能为 1 ,否则会抛出数据长度超出限制。
2、WordLen 输入 S7WLByte 实际上相当于我们常用的枚举,和当中的值无关。
3、 pUsrData中使用 “Byte” 类型读取时,所存储的数据端序为 “大端序” 。而我们PC通常所用 x86 架构的CPU为 “小端序” 存储;ARM 则默认为 “小端序”,但可配置端序 。C/C++编程的数据存储方式与机器所使用的CPU有关,所以我们需要自行转换端序。

这里为了方便读取一整个 Struct ,我使用的是一个 unsigned char 的数组,最后根据根据实际情况机型解析。

调试过程中,我在输入地址为十进制时,则会抛出一个 “errCliAddressOutOfRange” 的错误,填写十六进制地址时,则能够正常读写。目前未知具体原因。

3. 最后

Snap7库中还有很多其他的接口,需要之后慢慢去了解。

如有错误,请各位大佬斧正!😁

4. 调试笔记

  • 在因为增加需求,需要使用C++17的特性,而C++17中引入了一个 std::byte 这一类型。在snap7.h中作者使用了 typedef uint8_t byte; 对类型名称进行重命名,会导致编译不过。增加命名空间声明,或者是修改重命名的名称可解决编译报错。
  • 17
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值