jetson nano上编译与使用西门子PLC通讯库snap7

文章目录

  • 一.西门子snap7介绍
  • 二.西门子S7通讯介绍
  • 三.jetson nano编译snap7库
  • 四.Qt Cmake导入snap7库
  • 五.snap7主要函数说明
    • 1.与PLC建立连接
    • 2.读写PA区变量
    • 3.读写MK区变量
  • 六.通讯程序示例

一.西门子snap7介绍

官网:https://snap7.sourceforge.net/

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以太网通讯协议,用于西门子内部硬件通讯

这两者的传输报文是不一样的,如下图:
jetson nano上编译与使用西门子PLC通讯库snap7_第1张图片
西门子数存储到二进制时方式是大端模式(BIG-Endian),而我们的普通电脑常常为小端模式(Liitle-Endian)。
大端模式是指数据的低位保存在内存的高地址中,而数据的高位保存在内存的低地址中.
小端模式是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中。
例如:双字 DWORD 0X2F11214C

PLC:
在这里插入图片描述

PC:
在这里插入图片描述

所以数据需要进行转换

三.jetson nano编译snap7库

  1. 下载源代码

    下载地址:

    https://sourceforge.net/projects/snap7/files/1.4.2/

  2. 解压下载好的文件,进入如下图所示目录
    jetson nano上编译与使用西门子PLC通讯库snap7_第2张图片

  3. 空白处右键打开命令行,输入如下代码进行编译源代码以及安装

    sudo make -f arm_v6_linux.mk install
    

    jetson nano上编译与使用西门子PLC通讯库snap7_第3张图片

四.Qt Cmake导入snap7库

  1. 从example/cpp文件夹中拷贝snap7.h snap7.cpp两个文件到自己的项目中
    jetson nano上编译与使用西门子PLC通讯库snap7_第4张图片

  2. 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
})

jetson nano上编译与使用西门子PLC通讯库snap7_第5张图片
jetson nano上编译与使用西门子PLC通讯库snap7_第6张图片

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];

六.通讯程序示例

打包下载地址:https://download.csdn.net/download/qq_30150579/87382222

# 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 

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 
#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 
#include 
#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("读取失败"));
    }
}

jetson nano上编译与使用西门子PLC通讯库snap7_第7张图片

你可能感兴趣的:(jetson,nano,linxu,linux,java)