首页 > 其他分享 >西门子s7通信协议

西门子s7通信协议

时间:2024-09-06 14:14:10浏览次数:4  
标签:00 字节 s7 0x00 西门子 0x01 通信协议 byte 0x03

西门子s7通信协议

S7Comm(S7 Communication)是西门子专有的协议,是西门子S7通讯协议簇里的一种。 S7通信协议是西门子S7系列PLC内部集成的一种通信协议,是S7系列PLC的精髓所在。 它是一种运行在传输层之上的(会话层/表示层/应用层)、经过特殊优化的通信协议,其信息传输可以基于MPI网络、 PROFIBUS网络或者以太网 s7在TCP连接上后还需要进行两次握手 S7协议的TCP/IP实现依赖于面向块的ISO传输服务。S7协议被封装在TPKT和ISO-COTP协议中,这使得PDU(协议数据单元) 能够通过TCP传送。

S7协议帧结构
1 TPKT 会话层 主要设置版本号 预留号 报文总长度
2 COPT 表示层 设置PDU类型
3 s7协议 应用层 设置协议头和协议参数等

s7协议的使用
使用tcp连接时需要进行五次握手,其中有三次是tcp客户端与服务器的基有链接,然后需要再通过s7协议发送两次请求连接,共为五次握手。

TCP三次握手(TCP连接时进行) => COTP连接(第一次握手连接) => S7连接(第二次握手) => 数据的读写

连接
TCP三次握手(TCP连接时进行) => COTP连接(第一次握手连接) => S7连接(第二次握手) => 数据的读写

COTP连接(第一次握手)报文

 

 

S7连接(第二次握手)报文

 

 

使用tcp五次握手进行连接

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
 
    /// <summary>
    /// 五次握手
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void button1_Click(object sender, EventArgs e)
    {
        // s7协议
        // 1 需要通过socket三次握手,不用写握手过程
        // 目前提供设备型号s71200  cpu:1212c    电压是24vDC
        TcpClient client = new TcpClient();
        client.Connect("192.168.107.202",102); // 连接服务器
 
        receiveData(client);
 
        // 2 第一请求连接  发送请求帧为
        // 总共22个字节
        byte[] bs1 = new byte[]
        {
            0x03, // 1字节版本号 默认是03
            0x00, // 1字节 保留值 默认0
            0x00, 0x16, // 2 字节 报文的总长度
 
            0x11, // 1字节从该字节往后字节个数 十进制是17
            0xE0, // PDU 类型
            0x00,0x00, // DST引用 默认值
            0x00,0x01, // src引用
            0x00, // 采用默认值
            0xc1, // 上位机擦书
            0x02, // 上位机长度
            0x10,0x00, // 0x01代表双边通信 0x00机架号和插槽号
            
            0xC2, // plc参数
            0x02, // 长度
 
            0x03,0x01, // 0x01和0x00 共同控制机架号和插槽
            0xC0,0x01,
            0x0a
        };
        client.GetStream().Write(bs1,0,bs1.Length); // 发送第一次请求帧 
 
 
        // 3 第二次请求连接 发送请求帧为
        bs1 = new byte[]
        {
            0x03, // 1字节版本号 默认是03
            0x00, // 1字节 保留值 默认0
            0x00, 0x19, // 2 字节 报文的总长度
 
            0x02, // 当前字节后的字节数
            0xF0, // PUD类型 数据传输
            0x80, // 最高是十进制128
 
            0x32, // 协议ID,固定值
            0x01, // 工作类型 0x01 主站发送请求
            0x00,0x00,
            0x00,0x00,
            0x00,0x08, // 参数长度
            0x00,0x00, // 数据长度
 
            0xF0, // 功能码
            0x00, // Reserved保留值
            0x00,0x03, // 允许操作最大工作队列
            0x00,0x03, 
            0x03,0xc0, // 允许处理最大字节数组
        };
        client.GetStream().Write(bs1,0,bs1.Length);
        MessageBox.Show("连接成功");
 
    }
 
    /// <summary>
    /// 接收响应数据集
    /// </summary>
    /// <param name="tcpClient"></param>
    public void receiveData(TcpClient tcpClient)
    {
        Task.Run(() =>
        {
            byte[] bytes = new byte[1024];
            while (tcpClient.Connected)
            {
                // ONE
                int count = tcpClient.GetStream().Read(bytes,0,bytes.Length);
                if (count == 0) return;
                Console.WriteLine(BitConverter.ToString(bytes, 0, count) + "\r\n");
 
                // TWO
                byte[] s = new byte[count];
                Array.Copy(bytes,s,count);
                Console.WriteLine(string.Join(",",s));
            }
        });
    }
}

  

读取和写入报文格式

 

 

数据的读取

/// <summary>
/// 读取M区
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{
    // 发送请求帧 请求M区地址从00开始 读取一个数据
    // 读取数据时的请求帧
    byte[] data = new byte[] 
    {
        // TPKT: 版本号 预留号 总字节长度
        0x03, // 版本号 
        0x00, // 预留号
        0x00,0x01F, // 总字节长度
 
        // COTP: 
        0x02, // 往下的长度
        0xF0, // PDU类型
        0x80, // 目标引用
 
        // s7-header s7头
        0x32, // 协议ID 默认
        0x01, // 主站开始发请求
        0x00,0x00, // 预留位置
        0x03,0x7b, // 随机生成的数字 每次在基础之上递增
        0x00,0x0e, // 参数长度
        0x00,0x00, // 数据长度
 
        // s7-参数部分
        0x04, // 功能码 读取功能                 重点
        0x01, // 如果涉及多读时候 设置为1,
        0x12, // 结构表示 一般默认12
        0x0a, // 往后的字节长度
        0x10, // 寻址模式
        0x02, // 读取的数据类型 02是字节类型
        0x00,0x01, // 读取长度                   重点
        0x00,0x00, // 读取不是DB区               重点
        0x83, // 0x83 M存储区,0x84DB块          重点
        0x00,0x00,0x70, // 开始数据起始地址      重点
 
        // 列如M30000, 实际地址是30000*8=24 00000,把转成山歌字节,转成16进制3a980 对应三个地址,0x03,0xA9 0x80
        // 列如数据DB块的数据,DB21234,4000,其中DB号是21234 转成16进制0x52F2,DB区改为0x52,0xF2
        // 40000*8=,2000,转成16进制7d00 转成三个字节变成 0x00,0x7d,0x00
    };
    socket.Send(data);
}

  

接收数据的响应

/// <summary>
/// 接收响应数据
/// </summary>
void startReceive()
{
    Task.Run(() =>
    {
        byte[] bytes = new byte[1024];
        while (true)
        {
            int count = socket.Receive(bytes);
            if (count == 0) break;
 
            // 转为16进制的字符串
            Console.WriteLine("十六进制打印:"+BitConverter.ToString(bytes,0,count));
 
            // 转为十进制打印
            byte[] datas = new byte[count];
            Array.Copy(bytes,datas,count);
            Console.WriteLine("十进制打印:" + string.Join(",",datas));
 
            Invoke(new Action(() =>
            {
                try
                {
                    this.label1.Text = datas[25].ToString();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
            }));
 
 
            /* 响应的数据
            * 连接的响应
            * 03-00-00-16-11-D0-00-01-00-08-00-C0-01-0A-C1-02-10-00-C2-02-03-01
            * 03-00-00-1B-02-F0-80-32-03-00-00-00-00-00-08-00-00-00-00-F0-00-00-03-00-03-00-F0
            * 
            * 读取数据的响应
            * 03-00-00-1A-02-F0-80-32-03-00-00-03-7B-00-02-00-05-00-00-04-01-FF(读取成功的标志)-04(读取的数据类型)-00-08(数据的长度)-0C(数据)
            */
 
        }
    });
}

  

数据的写入

/// <summary>
/// 写入M14
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
    byte[] value = BitConverter.GetBytes(uint.Parse(textBox1.Text));
    // 生成写的报文
    byte[] bs = new byte[]
    {
        // TPKT部分
        0x03, // 版本号
        0x00, // 预留号
        // 0x00,0x24, // 报文总长度36
        0x00,0x27, // 报文总长度39
 
        // TOPT
        0x02, // 长度
        0xF0, // PDU类型
        0xB0, // 目标引用
 
        // s7 header
        0x32, // 协议id
        0x01, // 主站开始请求
 
        0x00,0x00, // 预留部分
 
        0x03,0x7d, // 随机生成
        0x00,0x0E, // 参数长度
        0x00,0x08, // 参数数据长度
 
        // s7 参数
        0x05, // 05代表写入,04代表读取
        0x01, // 通信项数 可以支持多写
        0x12, // 变量指定
        0x0A, // 后面的长度
        0x10,
        0x02, // 传输数据类型 字节
 
        // 0x00,0x01, // 操作数据的长度
        0x00,0x04, // 操作数据的长度
 
        0x00,0x00, // M区 不是DB区
        0x83, // M区
 
        // 0x00,0x00,0x70, // 开始写入的地址M14
        0x00,0x3e,0x80, // 开始写入的地址M2000
 
        0x00,
        0x04, // 字节类型
 
        // 0x00,0x80, // 写入的长度  8位=1字节
        0x00,0x20, // 写入的长度  8位=1字节 (写入4个数据 4*8=32转16进制为20)
 
        //byte.Parse(textBox1.Text)
        value[3],value[2],value[1],value[0]
    };
    tcp.Send(bs);
    Type = RequestType.Write;
}

  

完整代码

public partial class Form1 : Form
{
    TcpClientHelper tcp;
    public Form1()
    {
        InitializeComponent();
    }
 
    private void Tcp_OnClose(TcpClientHelper obj)
    {
        MessageBox.Show("客户端关闭");
    }
 
    enum RequestType
    {
        Write,  // 写入请求
        Read,   // 读取请求
        Connect // 连接的请求
    }
    RequestType Type;
 
 
    private void Tcp_OnMessage(byte[] arg1, TcpClientHelper arg2)
    {
        // 获取数据即可 自动触发
        BeginInvoke(new Action(() =>
        {
            switch (Type)
            {
                case RequestType.Write:
                    // 写入数据的响应
                    Console.WriteLine("写入数据的响应"+BitConverter.ToString(arg1) );
                    break;
                case RequestType.Read:
                    // 读取数据的响应
                    Console.WriteLine("读取数据的响应" + BitConverter.ToString(arg1));
                    this.label1.Text = arg1[arg1.Length-1].ToString();
                    break;
                case RequestType.Connect:
                    Console.WriteLine("连接时的响应" + BitConverter.ToString(arg1));
                    break;
                    
            }
        }));
    }
 
    /// <summary>
    /// 写入M14
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void button1_Click(object sender, EventArgs e)
    {
        byte[] value = BitConverter.GetBytes(uint.Parse(textBox1.Text));
        // 生成写的报文
        byte[] bs = new byte[]
        {
            // TPKT部分
            0x03, // 版本号
            0x00, // 预留号
            // 0x00,0x24, // 报文总长度36
            0x00,0x27, // 报文总长度39
 
            // TOPT
            0x02, // 长度
            0xF0, // PDU类型
            0xB0, // 目标引用
 
            // s7 header
            0x32, // 协议id
            0x01, // 主站开始请求
 
            0x00,0x00, // 预留部分
 
            0x03,0x7d, // 随机生成
            0x00,0x0E, // 参数长度
            0x00,0x08, // 参数数据长度
 
            // s7 参数
            0x05, // 05代表写入,04代表读取
            0x01, // 通信项数 可以支持多写
            0x12, // 变量指定
            0x0A, // 后面的长度
            0x10,
            0x02, // 传输数据类型 字节
 
            // 0x00,0x01, // 操作数据的长度
            0x00,0x04, // 操作数据的长度
 
            0x00,0x00, // M区 不是DB区
            0x83, // M区
 
            // 0x00,0x00,0x70, // 开始写入的地址M14
            0x00,0x3e,0x80, // 开始写入的地址M2000
 
            0x00,
            0x04, // 字节类型
 
            // 0x00,0x80, // 写入的长度  8位=1字节
            0x00,0x20, // 写入的长度  8位=1字节 (写入4个数据 4*8=32转16进制为20)
 
            //byte.Parse(textBox1.Text)
            value[3],value[2],value[1],value[0]
        };
        tcp.Send(bs);
        Type = RequestType.Write;
    }
 
    /// <summary>
    /// 读取M14
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void button2_Click(object sender, EventArgs e)
    {
        // 读取的报文31字节
        byte[] bs = new byte[]
        {
            0x03,
            0x00,
            0x00,0x1f,
 
            0x02,
            0xf0,
            0xb0,
 
            // 协议参数
            0x32,
            0x01,
            0x00,0x00,
            0x03,0x7d,
            0x00,0x0e,
            0x00,0x00, // 读取操作 为0
 
            0x04,0x01,
            0x12,0x0a,
            0x10,
            0x02,
 
            //0x00,0x01, // 读取长度
            0x00,0x04, // M2000 读取四个字节
 
            0x00,0x00,
            0x83,
 
            // 0x00, 0x00,0x70
            0x00,0x3e,0x80,
        };
        tcp.Send(bs);
        Type = RequestType.Read;
    }
 
    /// <summary>
    /// 连接
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Form1_Load(object sender, EventArgs e)
    {
        // 1 创建客户端对象
        tcp = new TcpClientHelper();
        tcp.Connect("192.168.107.202", 102); // 连接服务器
 
        // 2 获取数据
        tcp.OnMessage += Tcp_OnMessage;
 
        // 3 关闭连接
        tcp.OnClose += Tcp_OnClose;
 
        // 第一次连接请求
        byte[] bs = new byte[]
        {
            0x03, // 1字节版本号 默认是03
            0x00, // 1字节 保留值 默认0
            0x00, 0x16, // 2 字节 报文的总长度
 
            0x11, // 1字节从该字节往后字节个数 十进制是17
            0xE0, // PDU 类型
            0x00,0x00, // DST引用 默认值
            0x00,0x01, // src引用
            0x00, // 采用默认值
            0xc1, // 上位机擦书
            0x02, // 上位机长度
            0x10,0x00, // 0x01代表双边通信 0x00机架号和插槽号
            
            0xC2, // plc参数
            0x02, // 长度
 
            0x03,0x01, // 0x01和0x00 共同控制机架号和插槽
            0xC0,0x01,
            0x0a
        };
        tcp.Send(bs);
 
        // 第二次请求连接
        bs = new byte[]
        {
            0x03, // 1字节版本号 默认是03
            0x00, // 1字节 保留值 默认0
            0x00, 0x19, // 2 字节 报文的总长度
 
            0x02, // 当前字节后的字节数
            0xF0, // PUD类型 数据传输
            0x80, // 最高是十进制128
 
            0x32, // 协议ID,固定值
            0x01, // 工作类型 0x01 主站发送请求
            0x00,0x00,
            0x00,0x00,
            0x00,0x08, // 参数长度
            0x00,0x00, // 数据长度
 
            0xF0, // 功能码
            0x00, // Reserved保留值
            0x00,0x03, // 允许操作最大工作队列
            0x00,0x03,
            0x03,0xc0, // 允许处理最大字节数组
        };
        tcp.Send(bs);
        Type = RequestType.Connect;
        MessageBox.Show("连接成功");
    }
}

  

 

标签:00,字节,s7,0x00,西门子,0x01,通信协议,byte,0x03
From: https://www.cnblogs.com/funiyi816/p/18400138

相关文章

  • IPv6协议——互联网通信协议第六版
    引言  IPv6是互联网升级演进的必然趋势、网络技术创新的重要方向、网络强国建设的基础支撑。近些年,随着我国大力推动IPv6规模部署和应用,目前中国的IPv6渗透率已超过70%。对于车载以太网来说,目前IPv4是车载IP通信的主流协议,但随着车辆的智能化、网联化程度不断提高,IPv6协议应......
  • centos7 内核升级
    需要下载三个rpm包wgethttp://mirrors.coreix.net/elrepo-archive-archive/kernel/el7/x86_64/RPMS/kernel-lt-devel-4.4.215-1.el7.elrepo.x86_64.rpmwgethttp://mirrors.coreix.net/elrepo-archive-archive/kernel/el7/x86_64/RPMS/kernel-lt-headers-4.4.215-1.el7.elrepo.......
  • 74LS74分频电路实现
    一、简述74LS74是双D触发器(上升沿触发),其管脚图及功能如下:74LS74内含两个独立的D触发器,每个触发器具有数据输入(D)、置位输入(/PR)、复位输入(/CLR)、时钟输入(CP)和数据输出(Q、/Q)。/PR和/CLR的低电平会使输出预置或清除,而与其它输入端的电平无关。当/PR和/CLR均无效(高电平)时,符合建立......
  • Centos7.9安装Docker和Docker compose
    什么是docker环境Docker环境是指在计算机中安装和配置了Docker引擎的运行环境。Docker是一种容器化平台,它提供了一种轻量级的虚拟化技术,能够将应用程序及其依赖项打包成一个独立的容器,以实现快速部署、可移植性和易于管理的优势。(Docker环境提供了一种方便、可移植和隔离的方式来......
  • 使用centos7搭建Cloudreve,在这条路上我尝试过自建、Owncloud等,恰巧最近发现了 Cloudre
     1.首先开始之前配置一个华为的源vi/etc/yum.repos.d/openstack.repo#内容如下[base]name=basebaseurl=https://repo.huaweicloud.com/centos/7/os/x86_64/enable=1gpgcheck=0[extras]name=extraxbaseurl=https://repo.huaweicloud.com/centos/7/extras/x86_64/......
  • MQTT揭秘:为什么它是物联网首选的通信协议
    MQTT协议简介概览MQTT是一种基于发布/订阅模式的轻量级消息传输协议,专门针对低带宽和不稳定网络环境的物联网应用而设计,可以用极少的代码为联网设备提供实时可靠的消息服务。MQTT协议广泛应用于物联网、移动互联网、智能硬件、车联网、智慧城市、远程医疗、电力、石油与能源等领......
  • centos7 数据盘分区并挂载
    一、查看磁盘情况方式一lsblk注:根据上图已经确认有一个新的数据盘4T方式二fdisk-l注:根据上图已经确认有一个新的数据盘4T。二、使用parted命令创建新分区注:以上截图创建新分区完成,具体命令如下:1、选择数据盘输入:parted/dev/vdb2、转换gpt分区输入:mklabelgpt注:出......
  • 基于centos7.5安装mysql8
    @目录环境初始化部署mysql配置主从报错问题解决重启集群操作环境初始化mysql官网下载使用环境VMware17,centos7.5节点IPmysql01192.168.200.20mysql02192.168.200.21初始化两台节点;免密,主机名,主机映射等viinit.sh#!/bin/bash#定义节点信息NODES=("1......
  • Centos7 离线安装字体库
    一、获取依赖包1、准备另一台能联网的Linux机器(要配置yum,建议配阿里的,网上很多教程,很简单!)2、安装获取依赖包的命令,然后新建一个文件来存放依赖包#安装获取依赖包的命令yum-yinstallyum-utils#新建一个文件来存放依赖包mkdir-p/root/font_rpm#执行获取相关依赖命令yumdo......
  • 西门子HMI制作数码管显示数字
    因为前一阵子做了停车库的相关案例,突发奇想在HMI上做一个数码管显示剩余车辆的信息,今天上午没什么事情,索性就记录一下制作过程。数码管显示数字原理这是一个经典的数码管图片,我们对每个边编一个号码,想让他显示数字,就是控制对应的几个编号亮灭。比如数字‘0’,我们只需要让7......