文章目录
- 一.西门子snap7介绍
- 二.西门子S7通讯介绍
- 三.jetson nano编译snap7库
- 四.Qt Cmake导入snap7库
- 五.snap7主要函数说明
- 1.与PLC建立连接
- 2.读写PA区变量
- 3.读写MK区变量
- 六.通讯程序示例
一.西门子snap7介绍
Snap7 是一个基于以太网与S7系列的西门子PLC通讯的开源库。支持包括S7系列的S7-200、S7-200 Smart、S7-300、S7-400、S7-1200以及S7-1500的以太网通信。支持32/64位英特尔/ AMD的所有平台。 例如:
-
Windows ( 除了 windows Me和95);
-
Linux和类Linux(树莓派,UBeagleBone Black,DOO 等),
-
BSD;
-
Oracle Solaris ;
-
Apple OSX
支持语言也比较广:
-
Pascal;
-
C#;
-
C++;
-
C;
-
LabVIEW
-
Python;
-
Node.js
-
Java.
二.西门子S7通讯介绍
西门子S7系列PLC采用以下两种通讯方式:
- 开放式的TCP\IP,可以用于连接PLC与其他非西门子硬件
- 西门子自己开发的S7 Protocol以太网通讯协议,用于西门子内部硬件通讯
这两者的传输报文是不一样的,如下图:
西门子数存储到二进制时方式是大端模式(BIG-Endian),而我们的普通电脑常常为小端模式(Liitle-Endian)。
大端模式是指数据的低位保存在内存的高地址中,而数据的高位保存在内存的低地址中.
小端模式是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中。
例如:双字 DWORD 0X2F11214C
PLC:
PC:
所以数据需要进行转换
三.jetson nano编译snap7库
-
下载源代码
下载地址:
https://sourceforge.net/projects/snap7/files/1.4.2/
-
解压下载好的文件,进入如下图所示目录
-
空白处右键打开命令行,输入如下代码进行编译源代码以及安装
sudo make -f arm_v6_linux.mk install
四.Qt Cmake导入snap7库
-
从example/cpp文件夹中拷贝snap7.h snap7.cpp两个文件到自己的项目中
-
cmake设置如下
cmake_minimum_required(VERSION 3.5) project(snap7Test VERSION 0.1 LANGUAGES CXX) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(QT NAMES Qt5 REQUIRED COMPONENTS Widgets) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Gui) set(PROJECT_SOURCES main.cpp mainwindow.cpp mainwindow.h mainwindow.ui snap7.cpp snap7.h ) add_executable(${PROJECT_NAME} ${PROJECT_SOURCES}) target_link_libraries(${PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Gui) target_link_libraries(${PROJECT_NAME} PRIVATE libsnap7.so)
主要在下面这两个,一个是把snap7.h和snap7.cpp添加到自己的项目中,一个是添加libsnap7.so库到项目中
set(PROJECT_SOURCES main.cpp mainwindow.cpp mainwindow.h mainwindow.ui snap7.cpp snap7.h ) target_link_libraries(${PROJECT_NAME} PRIVATE libsnap7.so)
五.snap7主要函数说明
ConnectTo(const char *RemAddress, int Rack, int Slot);
//函数说明:通过PLC的IP地址*RemAddress建立连接。
// *RemAddress PLC的IP地址,
// Rack s7200SMART PLC的齿数,一般为0
// Slot s7200SMART PLC的槽数,一般为1
Disconnect();
//函数说明:断开PC与PLC的连接
ReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
//函数说明:读PLC某个区域的值
//Area表示内存区。取值0x84:V区 0x83:M区 0x82:O区 0x81:I区 0x1C:C区 0x1D:T区
//DBNumber表示区域号,只有在对DB块使用时才有用,默认设置为0
//Start表示起始地址。当函数功能为读bit时,int addr =Start/8; int bit = Start%8;
// 此时表示第addr地址的第Bit位。如Start = 8;则表示第1号地址的第0位,即addr.Bit(1.0);
//Amount表示要读取的数据长度,当函数类型为读bit时,只能为1
//WordLen决定函数的功能,函数功能有读位,字节,字,双字。取值: 0x1:Bit 0x2:Byte
// 0x4:Word 0x6:DW 0x8 : Real 0x1c : C区(16Bit) 0x1D:T区(16Bit)
//注意:读Word和DWord功能有问题,高位字节在前,低位字节在后(跟我们的程序反过来)
//*pUsrData表示数据缓冲区,读取的数据存入该缓冲区
WriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
//函数说明:写PLC某个区域的值
//Area表示内存区。取值0x84:V区 0x83:M区 0x82:O区 0x81:I区 0x1C:C区 0x1D:T区
//DBNumber表示区域号,只有在对DB块使用时才有用,默认设置为0
//Start表示起始地址。当函数功能为写bit时,int addr =Start/8; int bit = Start%8;
// 此时表示第addr地址的第Bit位。如Start = 8;则表示第1号地址的第0位,即addr.Bit(1.0);
//Amount表示要写的数据长度,当函数类型为写bit时,只能为1
//WordLen决定函数的功能,函数功能有写位,字节,字,双字。取值: 0x1:Bit 0x2:Byte
// 0x4:Word 0x6:DW 0x8 : Real 0x1c : C区(16Bit) 0x1D:T区(16Bit)
//注意:读Word和DWord功能有问题,高位字节在前,低位字节在后(跟我们的程序反过来)
//*pUsrData表示数据缓冲区,写入PLC的数据存入该缓冲区
DBRead(int DBNumber, int Start, int Size, void *pUsrData);
//函数说明:读V区的Byte值
//DBNumber读V区识别号码,只有在对DB块使用时才有用,默认设置为0
//Start读PLC的起始地址
//Size读PLC的字节个数
//*pUsrData数据缓冲区,函数读到的数据存在这个缓冲区内
DBWrite(int DBNumber, int Start, int Size, void *pUsrData);
//函数说明:写V区的Byte值
//DBNumber读V区识别号码,只有在对DB块使用时才有用,默认设置为0
//Start读PLC的起始地址
//Size读PLC的字节个数
//*pUsrData数据缓冲区,函数读到的数据存在这个缓冲区内
//area用于区分I、Q、M、DB区域,具体选择如下
areas = ADict({
'PE': 0x81, #input 输入区
'PA': 0x82, #output 输出区
'MK': 0x83, #bit memory 中间存储区(M区)
'DB': 0x84, #DB区
'CT': 0x1C, #counters
'TM': 0x1D, #Timers
})
1.与PLC建立连接
#include "snap7.h"
TS7Client* client = new TS7Client;
char *Address = "192.168.1.10";
int Rack = 0;
int Slot = 1;
client->ConnectTo(Address, Rack, Slot);
2.读写PA区变量
int l_byData[1] = { 1 };
client->WriteArea(S7AreaPA, 0, 4, 1, S7WLBit, &l_byData); //PA区的0.4写入值1
client->ReadArea(S7AreaPA, 0, 4, 1, S7WLBit, &l_byData); //PA区的0.4读取值1
3.读写MK区变量
byte l_byData[4] = { 0 };
float l_fSpeed = 50;
//字节转换
l_byData[3] = *((byte*)&l_fSpeed + 0);
l_byData[2] = *((byte*)&l_fSpeed + 1);
l_byData[1] = *((byte*)&l_fSpeed + 2);
l_byData[0] = *((byte*)&l_fSpeed + 3);
client->WriteArea(S7AreaMK, 0, 4, 4, S7WLDWord, &l_byData); //写入值
client->ReadArea(S7AreaMK, 0, 4, 4, S7WLDWord, &l_byData); //读取值
//转换
*((byte*)&l_fSpeed + 0) = l_byData[3];
*((byte*)&l_fSpeed + 1) = l_byData[2];
*((byte*)&l_fSpeed + 2) = l_byData[1];
*((byte*)&l_fSpeed + 3) = l_byData[0];
六.通讯程序示例
# cmakeList.txt
cmake_minimum_required(VERSION 3.5)
project(snap7Test VERSION 0.1 LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt5 REQUIRED COMPONENTS Widgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Gui)
set(PROJECT_SOURCES
main.cpp
mainwindow.cpp
mainwindow.h
mainwindow.ui
snap7.cpp
snap7.h
)
add_executable(${PROJECT_NAME} ${PROJECT_SOURCES})
target_link_libraries(${PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Gui)
target_link_libraries(${PROJECT_NAME} PRIVATE libsnap7.so)
//main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "snap7.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
TS7Client* client;
char *Address;
int Rack = 0;
int Slot = 1;
private slots:
void on_ConnectpushButton_clicked();
void on_DisconnectpushButton_clicked();
void on_WritePApushButton_clicked();
void on_ReadPApushButton_clicked();
void on_WriteMKpushButton_clicked();
void on_ReadMKpushButton_clicked();
};
#endif // MAINWINDOW_H
// mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QDebug>
#include <QMessageBox>
#include "snap7.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
client = new TS7Client;
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_ConnectpushButton_clicked()
{
QByteArray ad(ui->lineEdit->text().toUtf8());
Address = ad.data();
Rack = ui->lineEdit_2->text().toInt();
Slot = ui->lineEdit_3->text().toInt();
int tmp = client->ConnectTo(Address, Rack, Slot);
if(tmp==0)
{
QMessageBox::information(this,tr("success"),tr("PLC连接成功"));
}
else
{
QMessageBox::critical(this,tr("error"),tr("PLC连接失败"));
}
}
void MainWindow::on_DisconnectpushButton_clicked()
{
int tmp = client->Disconnect();
if(tmp==0)
{
QMessageBox::information(this,tr("success"),tr("PLC断开成功"));
}
else
{
QMessageBox::critical(this,tr("error"),tr("PLC断开失败"));
}
}
void MainWindow::on_WritePApushButton_clicked()
{
int l_byData[1] = { ui->lineEdit_6->text().toInt() };
int tmp = client->WriteArea(S7AreaPA, 0, ui->lineEdit_7->text().toInt(), 1, S7WLBit, &l_byData);
if(tmp==0)
{
QMessageBox::information(this,tr("success"),tr("写入成功"));
}
else
{
QMessageBox::critical(this,tr("error"),tr("写入失败"));
}
}
void MainWindow::on_ReadPApushButton_clicked()
{
int l_byData[1] = { 0 };
int tmp = client->ReadArea(S7AreaPA, 0, ui->lineEdit_7->text().toInt(), 1, S7WLBit, &l_byData);
if(tmp==0)
{
QMessageBox::information(this,tr("success"),tr("读取成功"));
ui->lineEdit_8->setText(QString("%1").arg(l_byData[0]));
}
else
{
QMessageBox::critical(this,tr("error"),tr("读取失败"));
}
}
void MainWindow::on_WriteMKpushButton_clicked()
{
byte l_byData[4] = { 0 };
float l_fSpeed = ui->lineEdit_5->text().toFloat();
l_byData[3] = *((byte*)&l_fSpeed + 0);
l_byData[2] = *((byte*)&l_fSpeed + 1);
l_byData[1] = *((byte*)&l_fSpeed + 2);
l_byData[0] = *((byte*)&l_fSpeed + 3);
int tmp = client->WriteArea(S7AreaMK, 0, ui->lineEdit_4->text().toInt(), 4, S7WLDWord, &l_byData);
if(tmp==0)
{
QMessageBox::information(this,tr("success"),tr("写入成功"));
}
else
{
QMessageBox::critical(this,tr("error"),tr("写入失败"));
}
}
void MainWindow::on_ReadMKpushButton_clicked()
{
byte l_byData[20] = {0};
float l_fSpeed = {0};
int tmp = client->ReadArea(S7AreaMK, 0, ui->lineEdit_4->text().toInt(), 4, S7WLDWord, &l_byData);
if(tmp==0)
{
QMessageBox::information(this,tr("success"),tr("读取成功"));
*((byte*)&l_fSpeed + 0) = l_byData[3];
*((byte*)&l_fSpeed + 1) = l_byData[2];
*((byte*)&l_fSpeed + 2) = l_byData[1];
*((byte*)&l_fSpeed + 3) = l_byData[0];
ui->lineEdit_9->setText(QString("%1").arg(l_fSpeed));
}
else
{
QMessageBox::critical(this,tr("error"),tr("读取失败"));
}
}