Wireshark CANopen 协议分析

Wireshark 是功能强大的网络协议分析工具,配合 CANopen 解码插件,可以直观地分析 CANopen 通信流程。

1. Wireshark 安装与配置

1.1 安装 Wireshark

# Ubuntu/Debian
sudo apt-get install wireshark

# Fedora
sudo dnf install wireshark

# macOS
brew install --cask wireshark

1.2 安装 CANopen 解码插件

Wireshark 默认支持 CAN 帧解析,但需要额外插件才能完整解码 CANopen 协议。

# 从源码安装 CANopen Wireshark 插件
git clone https://github.com/linnemann77/wireshark-canopen-plugin.git
cd wireshark-canopen-plugin
mkdir build && cd build
cmake ..
make
sudo make install

# 或者使用预编译版本(某些发行版)
sudo apt-get install wireshark-canopen

1.3 配置 CAN 接口

物理 CAN 接口

# 创建虚拟 CAN(用于测试)
sudo modprobe vcan
sudo ip link add dev vcan0 type vcan
sudo ip link set up vcan0

# 配置 SocketCAN 为非阻塞模式
sudo ip link set vcan0 mtu 72

在 Wireshark 中选择接口

  1. 启动 Wireshark
  2. 在捕获界面中选择 vcan0 接口
  3. 点击右上角的齿轮图标配置过滤器

2. Wireshark 基础操作

2.1 开始捕获

┌─────────────────────────────────────────┐
│  Wireshark 捕获界面                      │
├─────────────────────────────────────────┤
│  [☑] vcan0              [蓝色鲨鱼] [▼] │ ← 选择接口
│                                         │
│  [捕获过滤器] can                    │ ← 输入过滤器
│                                         │
│  [▶ 开始捕获]  [⚙ 设置]             │
└─────────────────────────────────────────┘

2.2 捕获过滤器

# 捕获所有 CAN 帧
can

# 只捕获特定 ID 范围
can.id == 0x123

# 捕获 CANopen NMT 消息
can.id == 0x000

# 只捕获节点 2 的消息
can.id >= 0x200 && can.id <= 0x27F

2.3 显示过滤器

# 显示所有 SDO 消息
can.opcode == 0x600 || can.opcode == 0x580

# 显示节点 2 的所有通信
can.node == 2

# 显示错误帧
can.flags.error == 1

# 显示心警报
can.opcode == 0x700 && can.data[0] == 0x80

3. CANopen 协议解析

3.1 NMT 协议分析

消息结构

CAN ID: 0x000
Data: [Command][Node ID]

Wireshark 显示示例

Frame 1: 8 bytes on wire (64 bits), 8 bytes captured (64 bits) on interface vcan0
CAN
    Identifier: 0x000
    Flags: Standard
    Data: 0102
CANopen
    Message Type: NMT Command
    NMT Command: Start Remote Node (0x01)
    Node ID: 2

常见 NMT 命令

命令值名称功能
0x01Start Remote Node启动远程节点
0x02Stop Remote Node停止远程节点
0x80Enter Pre-Operational进入预运行状态
0x81Reset Node节点复位
0x82Reset Communication通信复位

3.2 SDO 协议分析

SDO 请求帧(Client → Server)

CAN ID: 0x600 + Node ID
Data: [CCS][n][e][s][IndexHigh][IndexLow][SubIndex][Data...]

SDO 响应帧(Server → Client)

CAN ID: 0x580 + Node ID
Data: [SCS][n][e][s][IndexHigh][IndexLow][SubIndex][Data...]

Expedited 传输示例

Frame 10: 8 bytes on wire
CANopen
    Message Type: SDO Request
    Direction: Client -> Server
    Command Specifier: 0x23 (Initiate Download)
    Node ID: 2
    Expedited Transfer: Yes
    Size Indicated: Yes
    Data Size: 4 bytes
    Index: 0x1000 (Device Type)
    Sub-Index: 0x01
    Data: 0x12345678

Segmented 传输示例

Frame 20: SDO Request - Initiate Download
    Command Specifier: 0x21 (Initiate Download)
    Segmented Transfer: Yes
    Size Indicated: Yes
    Total Size: 64 bytes
    Index: 0x2000
    Sub-Index: 0x00

Frame 21: SDO Response - Initiate Download Response
    Command Specifier: 0x60 (Upload Segment Response)
    Toggle Bit: 0

Frame 22: SDO Request - Download Segment
    Command Specifier: 0x00 (Download Segment)
    Data: [7 bytes]

Frame 23: SDO Response - Download Segment Response
    Command Specifier: 0x20 (Download Segment Response)
    Toggle Bit: 1

... (重复直到完成)

SDO Abort 示例

Frame 100: SDO Response - Abort
    Command Specifier: 0x80 (Abort)
    Abort Code: 0x06020000 (Object does not exist)
    Index: 0x1234
    Sub-Index: 0x00

3.3 PDO 协议分析

TPDO (Transmit PDO)

Frame 50: TPDO
    CANopen
        Message Type: TPDO
        Node ID: 2
        PDO Number: 1
        COB-ID: 0x180 + 0x002 = 0x182
        Data:
            [0] Mapping 1: 0x1234 (Value)
            [1] Mapping 2: 0x5678 (Value)
            [2] Mapping 3: 0x9ABC (Value)
            [3] Mapping 4: 0xDEF0 (Value)

RPDO (Receive PDO)

Frame 60: RPDO
    CANopen
        Message Type: RPDO
        Node ID: 2
        PDO Number: 1
        COB-ID: 0x200 + 0x002 = 0x202

3.4 心跳协议分析

Frame 30: Heartbeat
    CANopen
        Message Type: Heartbeat
        Node ID: 2
        State: Operational (0x05)

心跳状态值

值状态说明
0x00Boot-up节点正在启动
0x04Stopped已停止
0x05Operational运行中
0x7FPre-Operational预运行状态

4. 高级分析技巧

4.1 统计功能

IO 图表

菜单: Statistics -> I/O Graph
用途: 可视化总线负载和消息频率

X 轴: 时间
Y 轴: 每秒帧数/字节数

配置示例:
- Graph 1: all packets, Packets/s, 蓝色
- Graph 2: can.opcode == 0x700, Packets/s, 绿色(心跳)
- Graph 3: can.opcode == 0x600 || 0x580, Packets/s, 红色(SDO)

对话统计

菜单: Statistics -> Conversations -> CAN
用途: 查看节点间通信统计

输出:
- 节点对 (Node A -> Node B)
- 交换的消息数
- 数据量
- 最小/最大/平均间隔

协议层级统计

菜单: Statistics -> Protocol Hierarchy
用途: 查看各协议消息分布

输出:
CANopen
├── NMT Command (5 packets, 0.5%)
├── Heartbeat (100 packets, 10%)
├── SDO (200 packets, 20%)
└── PDO (700 packets, 70%)

4.2 流跟踪

TCP 风格的会话跟踪

右键点击消息 -> Follow -> CANopen Stream

用途:
- 跟踪完整的 SDO 会话
- 查看 Request/Response 配对
- 分析时序关系

输出:
Frame 10: SDO Request (Read 0x1000:00)
Frame 11: SDO Response (Value: 0x00000192)
Time delta: 0.5ms

4.3 时间分析

消息间隔分析

# 计算特定消息的间隔时间
# 方法 1: 在 Wireshark 中
View -> Time Display Format -> Seconds since previous displayed packet

# 方法 2: 导出后分析
File -> Export Packet Dissections -> As JSON...

时序关系图

菜单: Statistics -> Flow Graph

类型: TCP Flow
显示:
- NMT Start -> Bootup -> Heartbeat 序列
- SDO Request -> SDO Response 时序
- PDO 发送频率

5. EDS 文件集成

5.1 什么是 EDS 文件?

EDS (Electronic Data Sheet) 文件描述了 CANopen 节点的对象字典结构,是理解 CANopen 通信的关键。

5.2 加载 EDS 文件

菜单: Edit -> Preferences -> Protocols -> CANopen

1. 添加 EDS 文件目录
2. 指定 EDS 文件位置
3. 映射节点 ID 到 EDS 文件

5.3 示例 EDS 映射

[1018]
ParameterName=Identity Object
ObjectType=0x8
DataType=0x0007
AccessType=rw
DefaultValue=0x00000000
PDOMapping=0

[1018sub0]
ParameterName=Number of Entries
ObjectType=0x7
DataType=0x0005
AccessType=rw
DefaultValue=4

加载后,Wireshark 会自动解析对象索引/子索引的含义。


6. 常见场景分析

6.1 分析 SDO 超时

问题: SDO 请求后无响应

分析步骤:

1. 查找 SDO Request 帧
2. 使用显示过滤器: can.opcode == 0x600
3. 查看后续是否有 SDO Response
4. 检查时间差是否超过超时时间
5. 查看是否有其他错误帧

# 查找超时的 SDO 请求
can.opcode == 0x600 && !frame.time_delta > 1.0

6.2 分析 PDO 更新频率

问题: PDO 更新不规律

分析步骤:

1. 过滤特定 TPDO: can.id == 0x182
2. 右键 -> Apply as Filter -> Selected
3. Statistics -> I/O Graph
4. 设置时间窗口为 1 秒
5. 观察波动模式

6.3 分析 NMT 状态转换

问题: 节点无法进入 Operational 状态

分析步骤:

1. 查找 NMT 命令: can.id == 0x000
2. 查找 Bootup 消息: can.id == 0x700 && can.data[0] == 0x00
3. 查找心跳消息: can.id == 0x7XX
4. 使用 Flow Graph 查看转换序列

# 正常序列
NMT Start -> Bootup -> Pre-Operational Heartbeat -> Operational Heartbeat

6.4 分析总线错误

问题: 频繁错误帧

分析步骤:

1. 查找错误帧: can.flags.error == 1
2. 查看错误统计: Statistics -> CAN Bus Errors
3. 分析错误类型:
   - Error Active: 节点主动报错
   - Error Passive: 节点被动状态
   - Bus Off: 总线关闭

# 查找变为 Bus Off 的帧
can.flags.error == 1 && can.data[0] == 0x00

7. 自动化分析脚本

7.1 使用 TShark(命令行)

# 统计各类型消息数量
tshark -r capture.pcap -T fields -e can.opcode | sort | uniq -c

# 导出 SDO 会话
tshark -r capture.pcap -Y "can.opcode == 0x600 || can.opcode == 0x580" \
       -T fields -e frame.number -e can.id -e can.data

# 查找超时的 SDO 请求
tshark -r capture.pcap -Y "can.opcode == 0x600" \
       -T fields -e frame.number -e can.id | \
while read num id; do
    next=$((num + 10))
    if ! tshark -r capture.pcap -Y "frame.number > $num && frame.number < $next && can.opcode == 0x580" | grep -q .; then
        echo "Frame $num (ID=$id): No SDO response"
    fi
done

7.2 Python 脚本分析

#!/usr/bin/env python3
# analyze_canopen.py - CANopen 日志分析

import pyshark
import sys

def analyze_sdo_sessions(cap):
    sdo_sessions = []
    current_session = None

    for packet in cap:
        if 'CANopen' in packet:
            if packet.can.opcode == '0x600':  # SDO Request
                if current_session:
                    sdo_sessions.append(current_session)
                current_session = {
                    'request_frame': int(packet.frame_info.number),
                    'request_id': packet.can.id,
                    'request_index': packet.canopen.index,
                    'request_subindex': packet.canopen.subindex,
                }
            elif packet.can.opcode == '0x580' and current_session:  # SDO Response
                current_session['response_frame'] = int(packet.frame_info.number)
                current_session['response_id'] = packet.can.id
                current_session['latency_ms'] = float(packet.frame_info.time_delta) * 1000
                sdo_sessions.append(current_session)
                current_session = None

    return sdo_sessions

def find_timeouts(sessions, timeout_ms=100):
    timeouts = [s for s in sessions if 'response_frame' not in s or s.get('latency_ms', 0) > timeout_ms]
    return timeouts

if __name__ == '__main__':
    cap = pyshark.FileCapture(sys.argv[1])
    sessions = analyze_sdo_sessions(cap)
    timeouts = find_timeouts(sessions)

    print(f"Total SDO sessions: {len(sessions)}")
    print(f"Timeouts: {len(timeouts)}")

    for t in timeouts[:5]:  # 显示前 5 个
        print(f"Frame {t['request_frame']}: No response (ID={t['request_id']})")

★ Insight ─────────────────────────────────────

  • Wireshark 的 IO Graph 是诊断总线负载问题的利器——正常 CANopen 总线负载应 < 80%,持续超过 90% 说明有严重问题。
  • SDO 会话跟踪功能可以快速定位超时原因:有时候不是没有响应,而是响应被丢弃或 ID 错误。
  • 加载 EDS 文件后,原始的 16 进制数据会被翻译成有意义的参数名,这是理解 CANopen 协议的关键步骤。 ─────────────────────────────────────────────────

下一步

→ 04-log-analysis.md —— 日志分析与自动化

如果这篇文章对你有帮助,欢迎请我喝杯咖啡 ☕
Cygnus Yang 支付宝支付宝
Cygnus Yang 微信微信