# canopen-sniffer **Repository Path**: bltzmnn/canopen-sniffer ## Basic Information - **Project Name**: canopen-sniffer - **Description**: 用Python实现的命令行解析canopen报文的小工具 - **Primary Language**: Python - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-07 - **Last Updated**: 2026-09-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: CANopen, Python ## README # CANopen Sniffer 一个类似 Wireshark 风格的 CANopen 协议分析器 TUI 工具,基于 Python 和 curses 构建。 通过 Linux SocketCAN 接口实时捕获和解码 CANopen 帧,支持彩色显示、过滤和基于 EDS 的 PDO 映射。专为嵌入式 ARM 开发板设计(Python ≥ 3.8,约 7 MB 磁盘,约 30 MB 内存)。 ## 功能特性 - **完整的 CANopen 协议解码** — NMT、SYNC、EMCY、PDO(TPDO/RPDO 1–4 + 自定义 PDO5+)、SDO(快速 / 分段 / 块传输 / 中止)、Heartbeat(心跳)、LSS - **基于 EDS 的自定义 PDO** — 加载 EDS/DCF 文件识别 TPDO5+、RPDO5+ 等非标准 COB-ID 的 PDO;将 PDO 数据解码为物理值 - **彩色显示** — 每种帧类型使用不同颜色;错误帧以加粗红色高亮 - **多条件过滤** — 支持按节点 ID、帧类型(包括自定义 PDO)或关键字搜索过滤 - **Wireshark 风格的详情面板** — 选中任意帧即可查看完整的协议解码 - **键盘驱动的 TUI** — 完整的键盘导航和快捷键 - **SocketCAN 原生支持** — 适用于任何 Linux CAN 接口 - **轻量级** — 8 个依赖,约 7 MB 磁盘,支持 Python 3.8 / aarch64 ## 截图 ``` No. Time COB-ID Node Type DLC Info 1 0.000s 0x000 - NMT 2 Start Remote Node → Node 1 2 0.012s 0x701 1 Heartbeat 1 State: Operational 3 0.015s 0x703 3 Heartbeat 1 State: Operational 4 0.050s 0x181 1 TPDO1 8 TxPDO1 [8B] 37 02 40 00 10 00 00 00 5 0.100s 0x080 - SYNC 2 Sync Counter: 1 6 0.200s 0x1CB 1 TPDO16 8 TxPDO16 [8B] 01 02 03 04 05 06 07 08 7 0.300s 0x601 1 SDO RX 8 Client→Server Download Expedited 0x1017:00 = 64 8 0.355s 0x583 3 SDO TX 8 Server→Client ABORT 0x1001:00 — Object does not exist 9 0.400s 0x081 1 EMCY 8 ⚠ 0x2000 Current | Reg: Current 10 0.500s 0x7E5 - LSS 8 Master→Slave Configure Node-ID = 7 -------------------------------------------------------------------------------- Frame #6 | COB-ID: 0x1CB | Node: 1 | TPDO16 Raw: 01 02 03 04 05 06 07 08 PDO Number: 16 Direction: Transmit Data Length: 8 bytes Data: 01 02 03 04 05 06 07 08 -- PDO Mapping (from EDS) -- Name Index Raw Physical Unit Type Voltage 0x2000:01 0x0102(258) 258.00 V UINT16 -------------------------------------------------------------------------------- CAPTURING | TAIL | Frames: 10 | Errors: 2 | Nodes: 1, 3 | can0 @ 500kbps ``` > 在真实终端中,每种帧类型会以其指定颜色渲染(NMT 为青色、SYNC 为蓝色、TPDO 为绿色、SDO 为黄色、**错误帧为加粗红色**等)。 ## 安装 ### 前置要求 - Python ≥ 3.8 - 带 SocketCAN 支持的 Linux 系统 - CAN 接口(真实硬件,如 can0) ### 快速安装(Makefile) ```bash git clone canopen-sniffer cd canopen-sniffer make install-user ``` ### 从源码安装(pip) ```bash pip install -e . ``` 依赖: | 依赖 | 用途 | |------|------| | [canopen](https://github.com/christiansandberg/canopen) ≥ 2.0 | EDS 解析与 PDO 映射 | | [python-can](https://python-can.readthedocs.io/) ≥ 4.0 | SocketCAN 接口 | | [click](https://click.palletsprojects.com/) ≥ 8.0 | CLI 参数解析 | ### 离线部署到嵌入式开发板 ```bash make offline-wheels OFFLINE_PLATFORM=manylinux2014_aarch64 OFFLINE_PYVER=3.8 rsync -avz offline_wheels/ root@192.168.32.77:/home/user/canopen-sniffer/offline_wheels/ ssh root@192.168.32.77 "pip3 install --user --no-index --find-links=offline_wheels/ canopen-sniffer" ``` ## 使用方法 ### 基本用法 ```bash canopen-sniffer -c can0 -b 500000 canopen-sniffer -c can1 -b 250000 ``` ### 使用 EDS 文件(自定义 PDO) 加载 EDS/DCF 文件解码 PDO 数据,并识别自定义 PDO(TPDO5+、RPDO5+ 等): ```bash canopen-sniffer -c can0 -e /path/to/device.eds 1 canopen-sniffer -c can0 -e drive.eds 1 -e io-module.eds 3 ``` 加载 EDS 后: - 非标准 COB-ID 的 PDO 帧(如 0x1CB 的 TPDO16)会被正确识别 - 详情面板显示解码后的 PDO 映射字段(名称、物理值、单位) - 按 `t` 切换到 "Custom PDO" 过滤器,只显示 PDO5+ 帧 ### 所有 CLI 选项 ``` Usage: canopen-sniffer [OPTIONS] CANopen Sniffer — Real-time CANopen protocol analyzer TUI. Options: -c, --channel TEXT SocketCAN interface name [default: can0] -b, --bitrate INTEGER CAN bus bitrate in bps [default: 500000] -e, --eds ... EDS file and node ID pair (repeatable) --help Show this message and exit. ``` ## 键盘快捷键 | 按键 | 操作 | |------|------| | `s` | 开始/暂停捕获 | | `c` | 清除所有捕获的帧 | | `n` | 切换节点 ID 过滤 | | `t` | 切换帧类型过滤(包括 "Custom PDO") | | `/` | 搜索输入(Enter 应用,Esc 取消) | | `↑` / `k` | 向上滚动 | | `↓` / `j` | 向下滚动 | | `PgUp` / `PgDn` | 滚动 10 行 | | `Home` | 跳到第一帧 | | `G` / `End` | 跳到最后一帧(自动滚动 / TAIL 模式) | | `q` | 退出 | > 向上滚动会切换到 FREE 模式(禁用自动滚动)。按 `G` 返回 TAIL 模式。 ## 颜色方案 | 帧类型 | 颜色 | |--------|------| | NMT | 青色 | | SYNC | 蓝色 | | EMCY | 加粗红色 | | TPDO1-4 | 绿色 | | RPDO1-4 | 暗绿色 | | 自定义 PDO(TPDO5+ / RPDO5+) | 绿色 | | SDO TX/RX | 黄色 | | Heartbeat(心跳) | 白色 | | LSS | 品红色 | | Unknown | 暗色 | | 错误帧(EMCY、SDO 中止) | 加粗红色 | ## 过滤功能 三个独立的过滤条件(AND 逻辑): 1. **节点过滤** — 按 `n` 切换已发现的节点 ID 2. **类型过滤** — 按 `t` 切换:All、NMT、SYNC、EMCY、TPDO1–4、RPDO1–4、SDO TX/RX、Heartbeat、LSS、Custom PDO 3. **关键字搜索** — 按 `/` 搜索描述、帧类型和 CAN ID ## 详情面板 导航到任意帧查看协议解码。详情格式因帧类型而异。 ### 自定义 PDO(含 EDS 映射) ``` Frame #6 | COB-ID: 0x1CB | Node: 1 | TPDO16 Raw: 01 02 03 04 05 06 07 08 PDO Number: 16 Direction: Transmit Data Length: 8 bytes Data: 01 02 03 04 05 06 07 08 -- PDO Mapping (from EDS) -- Name Index Raw Physical Unit Type Voltage 0x2000:01 0x0102(258) 258.00 V UINT16 ``` ### SDO 中止 ``` Frame #8 | COB-ID: 0x583 | Node: 3 | SDO TX Raw: 80 01 10 00 00 00 02 06 Index: 0x1001 Subindex: 0x00 Transfer: ABORT ABORT CODE: 0x06020000 Description: Object does not exist in the object dictionary ``` ## 项目结构 ``` canopen-sniffer/ ├── Makefile ├── pyproject.toml ├── src/canopen_sniffer/ │ ├── __init__.py │ ├── __main__.py # CLI 入口点(click) │ ├── curses_app.py # Curses TUI — 主应用、绘制、输入、过滤 │ ├── decoder.py # CANopen 协议解码器 │ ├── eds_manager.py # EDS 加载器、PDO 映射、OD 回退扫描 │ └── can_receiver.py # SocketCAN 接收器(多线程) └── tests/ └── test_decoder.py # 解码器单元测试(pytest) ``` ## 支持的 CANopen 服务 | 服务 | COB-ID 范围 | 描述 | |------|------------|------| | **NMT** | 0x000 | 网络管理 | | **SYNC** | 0x080 | 同步(带可选计数器) | | **EMCY** | 0x081–0x0FF | 紧急(错误码 + 寄存器 + 制造商数据) | | **TPDO1-4** | 0x181–0x4FF | 发送 PDO 1–4(标准) | | **RPDO1-4** | 0x201–0x57F | 接收 PDO 1–4(标准) | | **自定义 PDO** | EDS 定义 | TPDO5+ / RPDO5+,非标准 COB-ID | | **SDO TX** | 0x581–0x5FF | SDO 服务器→客户端(快速、分段、块传输、中止) | | **SDO RX** | 0x601–0x67F | SDO 客户端→服务器 | | **Heartbeat(心跳)** | 0x701–0x7FF | NMT 状态监控、启动检测 | | **LSS** | 0x7E4 / 0x7E5 | 层设置服务 | ## 部署到嵌入式开发板 ### 系统要求 | 项目 | 最低要求 | |------|---------| | Python | ≥ 3.8 | | 磁盘空间 | 约 7 MB | | 内存 | 运行时约 30 MB | | 内核 | 启用 SocketCAN | | 终端 | SSH 需要 `-t` 标志 | ### 安装 ```bash # 本地 make install # 远程(一条命令:构建 + rsync + pip 安装 + 复制到 /usr/bin/) make install IP=192.168.32.77 # 指定安装路径(默认 /usr/bin/) make install INSTALL_PATH=/usr/local/bin # 跨架构安装需先下载平台 wheel make offline-wheels OFFLINE_PLATFORM=manylinux2014_aarch64 OFFLINE_PYVER=3.8 make install IP=192.168.32.77 ``` ### 平台参数 | 开发板架构 | `OFFLINE_PLATFORM` | `OFFLINE_PYVER` | |-----------|---------------------|------------------| | ARM64 (aarch64) | `manylinux2014_aarch64` | `3.8` | | ARM32 (armv7l) | `manylinux2014_armv7l` | `3.8` | | x86_64 | `manylinux2014_x86_64` | `3.8` | ### 在开发板上运行 ```bash ssh -t root@192.168.32.77 canopen-sniffer -c can0 -e /root/device.eds 1 ``` ### 配置 SocketCAN ```bash sudo ip link set can0 type can bitrate 500000 sudo ip link set can0 up ``` ## 已知限制 - **仅被动监听** — 仅监听和解码;不发送 CAN 帧 - **仅支持经典 CAN** — 11 位 CAN ID,最多 8 字节数据(不支持 CAN FD) - **无状态解码器** — SDO 分段传输逐帧解码;未实现多帧重组 - **不支持节点守护** — 仅支持 Heartbeat(心跳)协议 - **仅限 Linux** — SocketCAN 是 Linux 特有的 ## 设计说明 ### 无 EDS 时的 PDO 解码 解码器通过 COB-ID 的功能码(CAN ID 高 7 位)识别 PDO 帧。例如,任何 `func_code = 0x180` 的 CAN ID 都被解码为 TPDO1,`node_id = can_id & 0x7F`。 这意味着非标准 COB-ID 如 `0x1C0`(设备可能分配给 TPDO5)会被解码为 **TPDO1 node 64**——因为 `0x1C0 & 0x780 = 0x180`,`0x1C0 & 0x7F = 64`。这符合 CANopen 基础协议规范,该规范只在标准 COB-ID 范围内定义了 TPDO1-4。 ### EDS 映射如何修正 加载 EDS 文件(`-e` 参数)后,程序对每个帧的 COB-ID 查询 EDS 对象字典。如果找到匹配: - **帧类型** 被修正(如 TPDO1 → TPDO5) - **节点 ID** 被修正(如 64 → 1,使用 EDS 中配置的 node ID) - **描述** 更新为正确的 PDO 名称 未加载 EDS 时,所有非标准 PDO COB-ID 都会显示为 TPDO1-4 / RPDO1-4,节点 ID 取自 CAN ID 低 7 位。 ### 动态 PDO 配置 主站通过 SDO 运行时配置的 PDO 映射(如写入 0x1800+ / 0x1A00+)**不会被跟踪**。程序只读取 EDS 文件中的默认值。要捕获运行时 PDO 配置,可在主站完成 SDO 配置后重启程序,或使用搜索功能(`/`)按 COB-ID 过滤。 ## 许可证 MIT