从零写一个工控多协议通讯库(四):西门子 S7,从 TPKT/COTP 到 DB1.DBW0
本文首发于我的博客talkplc.com系《从零写一个工控多协议通讯库》系列第四篇。转载请注明出处。第二篇把协议赶出了框架结尾立了个 flag接第二种协议时才见真章——如果新协议逼我改了框架那套“零协议、按索引”的抽象就没立住。这一篇来还债。选的是西门子S7它的地址是DB1.DBW0、M0.0、IW4和 Modbus 的“区域 寄存器号”是两个世界最适合拷问抽象。结论先说框架和界面一行没改加的只是一个新协议模块 一个驱动。项目代号talkplc。S7 部分从公开的 S7comm 协议从零写——只依据公开资料与抓包个人时间与设备不涉任何厂商代码。S7 不是“一层”是三层套娃Modbus 一帧很扁从站 功能码 数据 CRC。S7 classicS7-300/400/1200/1500 的非优化访问要啰嗦得多——它是三层套在一起┌─ TPKT (RFC1006) 03 00 [长度16] ──────────────────────────────┐ │ ┌─ COTP 02 F0 80 ───────────────────────────────────┐ │ │ │ ┌─ S7comm PDU 32 01 [冗余id] [ref] [参数长] [数据长] ───┐ │ │ │ │ │ 参数区: 04 01 S7ANY 寻址项… ← 这是一次 Read Var │ │ │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ └──────────────────────────────────────────────────────────────┘ │ └────────────────────────────────────────────────────────────────────┘ 整套跑在 tp_transport 字节管道上tp_tcp → PLC 的 102 端口TPKTRFC10064 字节小头就干一件事——用一个长度字段告诉你这一包多长TCP 是字节流得自己定界COTPISO 传输层数据帧固定02 F0 80S7comm PDU真正的应用层功能码Read Var / Write Var / Setup Communication……好在传输早就被抽象成字节管道了第一篇就定的所以这三层的组帧解帧和 Modbus TCP 一样全都架在同一个tp_transport上——协议层根本不知道底下是 socket。连上之前得先握两次手Modbus 连上就能读。S7 不行得先走两步握手TCP 连到 PLC:102 ├─ 发 COTP 连接请求(CR) —— TSAP 里编码了机架/槽号(rack/slot) │ 收 COTP 连接确认(CC) └─ 发 S7 Setup Communication —— 和 PLC 协商最大 PDU 长度 收 Ack比如协商成 960 字节 之后才能 Read Var / Write Varrack/slot是 S7 特有的概念S7-300 常是 0/2S7-1200/1500 常是 0/1它被塞进 COTP 的 TSAP 字段里。对外我的接口就一个tp_s7_connect()把这两步都包了tp_s7_t*s7tp_s7_new(transport,/*rack*/0,/*slot*/1);tp_s7_connect(s7);/* COTP CR/CC Setup Communication */uint8_tbuf[4];tp_s7_read_area(s7,TP_S7_AREA_DB,/*db*/1,/*start*/0,/*size*/4,buf);/* 读 DB1.DBD0 */和 Modbus 主站是一样的味道建在 transport 上、set_timeout/set_trace、读写区域。地址的世界观DB1.DBW0这才是拷问抽象的地方。Modbus 说“保持寄存器第 0 号”S7 说的是DB1.DBW0——1 号数据块、字节偏移 0、按字(word)读。还有M0.0标志位、IW4输入字、Q0.1输出位……一次DB1.DBW0的读在 S7comm 里被编码成一个 12 字节的S7ANY 寻址项12 0A 10 02 00 02 00 01 84 00 00 00 │ │ │ │ └─┬─┘ └─┬─┘ │ └──┬───┘ │ │ │ │ │ │ │ └ 起始地址(按“位”计) 字节0×8 0 │ │ │ │ │ │ └────── 区域码 0x84 DBM0x83 / I0x81 / Q0x82 │ │ │ │ │ └────────── DB 号 1 │ │ │ │ └──────────────── 元素个数 2 字节 │ │ │ └───────────────────── 传输尺寸 BYTE │ │ └──────────────────────── 语法 id S7ANY │ └─────────────────────────── 后续长度 10 └────────────────────────────── 寻址项标志 0x12关键点这套地址结构Modbus 的“区域 16 位地址”模型根本装不下。如果我的框架接口里还残留着 Modbus 的地址概念这里就得动框架。但框架真没动——只加了一个“地址串”第二篇里框架↔驱动的接口已经是按索引、零协议的了框架只管“把点位表交下去、按索引采集、把值收上来”从不解读地址。这次唯一的动作是给点位加一个通用地址串字段框架依旧不看它只透传/* 点位框架把它当不透明数据地址串只有对应协议的驱动才解析 */typedefstructtp_tag{charname[32];/* … 类型 / 字序 / 值 / 优先级 … */charaddr[24];/* DB1.DBW0 / M0.0 —— S7 驱动自己解析 */}tp_tag_t;于是 S7 的接入完全复刻 Modbus 的套路写一个S7 驱动实现那套索引接口内部把addr解析成(区域, DB号, 偏移, 位)、按大端解码、连续点位用一帧ReadMultiVars批量读在注册表里加一行s7 → S7 驱动工厂在协议清单protocols.json里加一行。框架、调度、点表、看板、配置加载——一行没改。界面里协议树自动多出“Siemens S7”点表填上DB1.DBW0值就按索引显示出来没有 PLC 也能测和 Modbus 一样我写了个内存 S7 从站应答 COTP CR/CC、Setup、Read/Write Var插在同一个tp_transport上——不接设备就能把 TPKT/COTP/S7 整条帧路跑通。于是一个无界面的小程序加载一份 S7 配置就能按索引把值打印出来$ config_monitor config/example_s7_sim.json --- cycle 0 [已连接] --- [0] temp 20 (ok) ← DB1.DBD0 float32 [1] count 17 (ok) ← DB1.DBW10 uint16 [2] run true (ok) ← M20.0 booltemp每拍在变、count每 4 拍才变——因为优先级调度高频/中频错开也是框架的事S7 驱动同样白捡。单元测试里这条“客户端 ↔ 内存从站”的往返连接协商、DB 读写回、M 位、float32自然也纳入了 CI。顺带一帧读多个点S7 的 Read Var 一帧里能放多个 S7ANY 项所以驱动轮询时把“这一拍要采的点”打包成一次ReadMultiVars最多 ~20 项而不是一个点发一帧——少很多往返。解析响应时要留意 S7 那个“项之间的填充字节”属于协议细节里的小坑。抽象立住了回到开篇的那个赌注接第二种协议会不会逼我改框架传输层没改S7 复用tp_transport/tp_tcp框架层没改还是按索引调度 中继界面层没改协议树、点表、看板、配置全靠数据驱动新增的只有talkplc_s7协议 一个 S7 驱动 注册表里一行。这正是前三篇一步步“把协议赶出去”想换来的东西第 N 个协议的接入成本和第二个一样低。地址是40001还是DB1.DBW0框架一视同仁——因为它压根不看。接下来S7 只做了classic非优化 DB。S7-1200/1500 的优化访问 / S7-Plus是另一套带加密的私有协议开源实现里也没有暂不碰。后面大概率往这几个方向走三菱MC、欧姆龙FINS——再各拷问抽象一次或回到界面把点位表做成协议无关的地址串编辑现在 S7 点位靠配置文件下发手动编辑还带着 Modbus 的“区域”列或做LVGL 嵌入式前端让这套纯 C 内核直接跑到 HMI 上。每加一层都回来对照一次改动越小说明当初的抽象越对。到目前为止它还立着。

相关新闻

Agent Token 成本治理:预算收紧时的估算与优化路径

Agent Token 成本治理:预算收紧时的估算与优化路径

Agent Token 成本治理:预算收紧时的估算与优化路径 Agent 的成本失控通常不是模型突然变贵,而是请求边界没有收住:每轮都附带完整历史、全部工具 Schema 和无关检索片段。调用次数上来后,单次多出的几百个 Token 会被放大成持续支…

2026/9/17 14:15:46 阅读更多 →
Linux系统上电自启与Systemd服务配置实战指南

Linux系统上电自启与Systemd服务配置实战指南

1. 项目概述:为什么“上电开机自运行”是嵌入式与工控的基石刚入行做嵌入式开发或者工业控制的朋友,可能都遇到过这样的需求:设备一插上电,就要像家里的电视一样,自己“滴”一声启动起来,然后默默地在后台把…

2026/9/20 20:04:41 阅读更多 →
XGP存档提取器:5分钟免费实现Xbox Game Pass游戏存档迁移的终极指南

XGP存档提取器:5分钟免费实现Xbox Game Pass游戏存档迁移的终极指南

XGP存档提取器:5分钟免费实现Xbox Game Pass游戏存档迁移的终极指南 【免费下载链接】XGP-save-extractor Python script to extract savefiles out of Xbox Game Pass for PC games 项目地址: https://gitcode.com/gh_mirrors/xg/XGP-save-extractor 你是否…

2026/9/13 5:27:53 阅读更多 →

最新新闻

Lucky 部署与功能实操指南:端口转发、DDNS 与反向代理配置

Lucky 部署与功能实操指南:端口转发、DDNS 与反向代理配置

Lucky 部署与功能实操指南:端口转发、DDNS 与反向代理配置 【免费下载链接】lucky 软硬路由公网神器,ipv6/ipv4 端口转发,反向代理,DDNS,WOL,ipv4 stun内网穿透,cron,acme,rclone,ftp,webdav,filebrowser 项目地址: https://gitcode.com/GitHub_Trending/luc/luck…

2026/9/20 20:03:46 阅读更多 →
PyPTO `get_spr` 特殊寄存器读取 API 详解:读取 AddrReg 字节数实现压缩数据长度感知

PyPTO `get_spr` 特殊寄存器读取 API 详解:读取 AddrReg 字节数实现压缩数据长度感知

PyPTO get_spr 特殊寄存器读取 API 详解:读取 AddrReg 字节数实现压缩数据长度感知 【免费下载链接】pypto PyPTO(发音: pai p-t-o):Parallel Tensor/Tile Operation编程范式。 项目地址: https://gitcode.com/cann/pypto …

2026/9/20 20:03:46 阅读更多 →
Cap 开源屏幕录制工具完整教程:免费录制、剪辑、分享,4 种数据存储方案

Cap 开源屏幕录制工具完整教程:免费录制、剪辑、分享,4 种数据存储方案

Cap 开源屏幕录制工具完整教程:免费录制、剪辑、分享,4 种数据存储方案 【免费下载链接】Cap Open source Loom alternative. Beautiful, shareable screen recordings. 项目地址: https://gitcode.com/GitHub_Trending/cap1/Cap Loom 是最流行的…

2026/9/20 20:03:46 阅读更多 →
GetQzonehistory实操指南:3步备份QQ空间全部历史说说

GetQzonehistory实操指南:3步备份QQ空间全部历史说说

GetQzonehistory实操指南:3步备份QQ空间全部历史说说 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 整理旧照片时翻到一张 2014 年的空间留言截图,你想查一下那…

2026/9/20 20:02:46 阅读更多 →
ChatTTS-ui:本地语音合成部署与API集成实战指南

ChatTTS-ui:本地语音合成部署与API集成实战指南

ChatTTS-ui:本地语音合成部署与API集成实战指南 【免费下载链接】ChatTTS-ui 一个简单的本地网页界面,使用ChatTTS将文字合成为语音,同时支持对外提供API接口。A simple native web interface that uses ChatTTS to synthesize text into spe…

2026/9/20 20:02:46 阅读更多 →
DBX Database Recipe 模板详解:为 90+ 数据库构建可复现的 Docker 测试环境

DBX Database Recipe 模板详解:为 90+ 数据库构建可复现的 Docker 测试环境

DBX Database Recipe 模板详解:为 90 数据库构建可复现的 Docker 测试环境 【免费下载链接】dbx 20 MB lightweight cross-platform database client for 90 databases, including MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, SQL Server, and Dameng. Bui…

2026/9/20 20:02:46 阅读更多 →

日新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/20 0:00:46 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/20 0:00:46 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/20 0:00:46 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/20 0:00:46 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/20 0:00:46 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/20 0:00:46 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/19 23:01:36 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/19 17:50:38 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/19 23:35:34 阅读更多 →