0xCAFEDECAF/VanBus

GitHub: 0xCAFEDECAF/VanBus

VanBus 是一个用于读写 PSA 车辆 VAN 总线数据包的 Arduino 库。

Stars: 74 | Forks: 14

PSA (标致、雪铁龙) VAN 总线读写器(适用于 Arduino-ESP8266 和 ESP32)

读取和写入 PSA 车辆 VAN 总线上的数据包。

[![Release Version](https://img.shields.io/github/release/0xCAFEDECAF/VanBus.svg?style=plastic)](https://github.com/0xCAFEDECAF/VanBus/releases/latest/) [![Release Date](https://img.shields.io/github/release-date/0xCAFEDECAF/VanBus.svg?style=plastic)](https://github.com/0xCAFEDECAF/VanBus/releases/latest/) [![arduino-library-badge](https://www.ardu-badge.com/badge/VanBus.svg)](https://www.ardu-badge.com/VanBus) [![Framework](https://img.shields.io/badge/Framework-Arduino-blue)](https://www.arduino.cc/) [![Runs the Arduino Linter](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/09f907390c002227.svg)](https://github.com/0xCAFEDECAF/VanBus/actions/workflows/arduino-lint.yml) [![Compile Library and Build Example Sketches](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/91ba3cca1d002228.svg)](https://github.com/0xCAFEDECAF/VanBus/actions/workflows/compile.yml) ## 📝 目录 - [描述](#description) - [原理图](#schematics) - [构建您的项目](#building) - [使用](#usage) - [概述](#general) - [功能函数](#functions) - [待完成工作](#todo) - [许可证](#license) ## 🎈 描述 此模块允许您在您的标致或雪铁龙车辆的 ["VAN" 总线] 上接收和发送数据包。 在 2000 年代初期,PSA 集团(标致和雪铁龙)使用 VAN 总线作为 各种舒适性相关设备之间的通信协议。后来,大约在 2005 年,他们开始在其新车型中使用 CAN 总线协议替代此协议,然而某些车型直到 2009 年仍在使用 VAN 总线。 [此概述](https://github.com/morcibacsi/PSAVanCanBridge#compatibility) 列出了据称 配备 VAN(舒适)总线的车辆。 此库支持 ESP8266 / ESP8285 和 ESP32 平台。 ## 🔌 原理图 通常可以在您主机(汽车音响)的 ISO "A" 接口模块的引脚 2 和 3 上找到 VAN 总线。参见 http://web.archive.org/web/20230315215552/https://en.wikipedia.org/wiki/Connectors_for_car_audio 以及 https://github.com/morcibacsi/esp32_rmt_van_rx#schematics 。 有多种方式将基于 ESP8266/ESP32 的开发板连接到您车辆的 VAN 总线: 1. 使用 [MCP2551] 收发器,将其 CANH 和 CANL 引脚连接到车辆的 VAN 总线。 由于 MCP2551 具有 5V 逻辑,需要一个 5V ↔ 3.3V [电平转换器] 将收发器的 CRX / RXD / R 引脚通过电平转换器连接到您的 ESP8266/ESP32 开发板的 GPIO 引脚。对于发送数据包,还需将收发器的 CTX / TXD / D 引脚通过电平转换器连接到您的 ESP8266/ESP32 开发板的 GPIO 引脚。 搭载 MCP2551 收发器的开发板可以在例如 [这里](https://domoticx.net/webshop/can-bus-classic-20b-transceiver-module-5v-uart-mcp2551/) 或 [这里](https://nl.aliexpress.com/item/1005004475976642.html) 订购。 使用 [Wemos D1 mini](基于 ESP8266)的示例原理图: ![schema](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/62dd3a5f11002229.png) 使用 [LilyGO TTGO T7 Mini32](基于 ESP32)的示例原理图: ![schema](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/c502cff95d002229.png) 2. 使用 [SN65HVD230] 收发器,将其 CANH 和 CANL 引脚连接到车辆的 VAN 总线。 SN65HVD230 收发器已具备 3.3V 逻辑,因此可以直接将收发器的 CRX / RXD / R 引脚连接到您的 ESP8266/ESP32 开发板的 GPIO 引脚。对于发送数据包,还需将收发器的 CTX / TXD / D 引脚连接到您的 ESP8266/ESP32 开发板的 GPIO 引脚。 搭载 SN65HVD230 收发器的开发板可以在例如 [这里](https://domoticx.net/webshop/can-bus-classic-20b-transceiver-module-33v-uart-sn65hvd230-vp230/), [这里](https://domoticx.net/webshop/can-bus-classic-20b-transceiver-module-33v-uart-sit65hvd230dr-tvs-esd-protection-domoticx-experience-ect01-v1/) 或 [这里](https://eu.robotshop.com/products/waveshare-can-board-sn65hvd230) 订购。 ![schema](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/3165428406002230.png) 3. 最简单的原理图是完全不使用收发器,而是使用两个 4.7 kOhm 电阻将 VAN 数据线连接到地。将您的 ESP8266/ESP32 开发板的 GPIO 引脚连接到由这两个电阻形成的 1:2 [分压器]。这仅用于接收数据包,不用于发送。结果可能有所不同。 ![schema](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/18685641e5002231.png) ## 🚀 构建您的项目 在继续此项目之前,请确保检查以下所有先决条件。 ### 安装 Arduino IDE 参见 [Arduino IDE](https://www.arduino.cc/en/software)。我目前使用的是 [版本 1.8.19](https://downloads.arduino.cc/arduino-1.8.19-windows.exe),但其他版本 可能也能正常工作。 ### 基于 ESP8266 的开发板 一个基于 ESP8266 的开发板示例是 [Wemos D1 mini]。 #### 1. 在 Arduino IDE 中安装开发板支持包 对于基于 ESP8266 的开发板,您需要安装 [ESP8266 开发板支持包](https://arduino-esp8266.readthedocs.io/en/stable/installing.html)。 我目前使用的是 [版本 3.1.2](https://github.com/esp8266/Arduino/releases/tag/3.1.2),但其他版本 似乎也能正常工作(我测试过 2.6.3 ... 3.1.2 版本)。 按照 [此教程](https://randomnerdtutorials.com/how-to-install-esp8266-board-arduino-ide/) 安装 ESP8266 开发板支持包。 #### 2. 安装 VAN Bus 库 在 Arduino IDE 中,转到 "Sketch" 菜单 → "Include Library" → "Manage Libraries..." 并安装 [车辆局域网 (VAN) 总线数据包读写器](https://github.com/0xCAFEDECAF/VanBus) 库。提示: 在搜索框中输入 "vanbus"。 有关使用 Arduino 库管理器的更多说明,您可以浏览: * 此 [Arduino 教程](https://docs.arduino.cc/software/ide-v1/tutorials/installing-libraries),以及 * 此 [Adafruit 的说明](https://learn.adafruit.com/adafruit-all-about-arduino-libraries-install-use/library-manager) #### 3. 开发板设置 在 Arduino IDE 中,转到 "Tools" 菜单,并选择: * CPU 频率:160 MHz 以下是经过测试可正常工作的开发板设置图片: ![Board settings](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/f308768e32002231.png) ### 基于 ESP32 的开发板 一个基于 ESP32 的开发板示例是 [LilyGO TTGO T7 Mini32]。 #### 1. 在 Arduino IDE 中安装开发板支持包 对于基于 ESP32 的开发板,您需要安装 ESP32 开发板支持包。 我目前使用的是 [版本 3.3.5](https://github.com/espressif/arduino-esp32/releases/tag/3.3.5),但其他版本 可能也能正常工作。(我测试过版本 1.0.6, 2.0.17 和 3.3.5。) 按照 [此教程](https://randomnerdtutorials.com/installing-the-esp32-board-in-arduino-ide-windows-instructions/) 安装 ESP32 开发板支持包。或者,转向 [此页面](https://docs.espressif.com/projects/arduino-esp32/en/latest/installing.html) 获取说明。 #### 2. 安装 VAN Bus 库 参见上文 ['安装 VAN Bus 库' 部分](#install_library)。 #### 3. 开发板设置 在 Arduino IDE 中,转到 "Tools" 菜单,并选择: * CPU 频率:240 MHz 以下是经过测试可正常工作的开发板设置图片: ![Board settings](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/315c30ec2e002232.png) ## 🧰 使用 ### 概述 将以下行添加到您的 ```.ino``` 草图中: ``` #include ``` 对于接收和发送数据包: 1. 将以下行添加到您的初始化代码块 ```void setup()``` 中: ``` int TX_PIN = D3; // GPIO pin connected to VAN bus transceiver input int RX_PIN = D2; // GPIO pin connected to VAN bus transceiver output TVanBus::Setup(RX_PIN, TX_PIN); ``` 2. 例如,将以下行添加到您的主循环 ```void loop()``` 中: ``` TVanPacketRxDesc pkt; if (VanBus.Receive(pkt)) pkt.DumpRaw(Serial); uint8_t rmtTemperatureBytes[] = {0x0F, 0x07, 0x00, 0x00, 0x00, 0x00, 0x70}; VanBus.SyncSendPacket(0x8A4, 0x08, rmtTemperatureBytes, sizeof(rmtTemperatureBytes)); ``` 如果您只想接收数据包,可以通过直接使用 ```VanBusRx``` 对象节省几百个宝贵的字节: 1. 将以下行添加到您的初始化代码块 ```void setup()``` 中: ``` int RX_PIN = D2; // GPIO pin connected to VAN bus transceiver output VanBusRx.Setup(RX_PIN); ``` 2. 将以下行添加到您的主循环 ```void loop()``` 中: ``` TVanPacketRxDesc pkt; if (VanBusRx.Receive(pkt)) pkt.DumpRaw(Serial); ``` ### ```VanBus``` 对象 ```VanBus``` 对象提供以下方法: 用于接收和发送数据包的接口: 1. [```void Setup(uint8_t rxPin, uint8_t txPin)```](#setup) 2. [```void DumpStats(Stream& s, bool longForm = true)```](#dumpstats) 用于接收数据包的接口: 3. [```bool Available()```](#available) 4. [```bool Receive(TVanPacketRxDesc& pkt, bool* isQueueOverrun = NULL)```](#receive) 5. [```uint32_t GetRxCount()```](#getrxcount) 6. [```int QueueSize()```](#queuesize) 7. [```int GetNQueued()```](#getnqueued) 8. [```int GetMaxQueued()```](#getmaxqueued) 9. [```void SetDropPolicy(int startAt, bool (*isEssential)(const TVanPacketRxDesc&) = 0)```](#setdroppolicy) 用于发送数据包的接口: 10. [```bool SyncSendPacket(uint16_t iden, uint8_t cmdFlags, const uint8_t* data, size_t dataLen, unsigned int timeOutMs = 10)```](#syncsendpacket) 11. [```bool SendPacket(uint16_t iden, uint8_t cmdFlags, const uint8_t* data, size_t dataLen, unsigned int timeOutMs = 10)```](#sendpacket) 12. [```uint32_t GetTxCount()```](#gettxcount) #### 1. ```void 设置(uint8_t rxPin, uint8_t txPin)``` 在 GPIO 引脚 ```rxPin``` 上启动接收器监听。发射器将在 GPIO 引脚 ```txPin``` 上发送。 #### 2. ```void 导出统计(Stream& s, bool longForm = true)``` 将一些数据包统计信息转储到传递的流中。向 `longForm` 参数传递 **false** 将生成简短形式。 #### 3. ```bool 可用()``` 如果接收队列中有可用的 VAN 数据包,则返回 ```true```。 #### 4. ```bool 接收(TVanPacketRxDesc& pkt, bool* isQueueOverrun = NULL)``` 如果可用,从接收队列中复制一个 VAN 数据包。否则,返回 ```false```。 如果向 'isQueueOverrun' 传递了有效指针,则会报告并清除任何队列溢出条件。 #### 5. ```uint32_t 获取接收计数()``` 返回自开机以来接收的 VAN 数据包数量。计数器可能会溢出回绕。 #### 6. ```int 队列大小()``` 返回在数据包丢失之前可以排队的 VAN 数据包数量。 #### 7. ```int 获取已排队数量()``` 返回当前已排队的 VAN 数据包数量。 #### 8. ```int 获取最大排队数量()``` 返回曾经排队的最高 VAN 数据包数量。 #### 9. ```void 设置丢弃策略(int startAt, bool (*isEssential)(const TVanPacketRxDesc&) = 0)``` 为接收队列开始填满时实现一个简单的数据包丢弃策略。 如果接收队列中有 ```startAt``` 个或更多数据包排队,则会丢弃数据包,除非该数据包被标识为“重要”。要标识此类数据包,请通过 ```isEssential``` 参数传递一个函数指针。传递的函数在中断上下文中调用,因此它*必须*具有 ```ICACHE_RAM_ATTR``` 属性。 示例: ``` bool ICACHE_RAM_ATTR IsImportantPacket(const TVanPacketRxDesc& pkt) { return pkt.DataLen() >= 3 && ( pkt.Iden() == CAR_STATUS1_IDEN // Right-hand stalk button press || (pkt.Iden() == DEVICE_REPORT && pkt.Data()[0] == 0x8A) // head_unit_report, head_unit_button_pressed ); } // IsImportantPacket #define RX_PIN D2 #define VAN_PACKET_QUEUE_SIZE 60 VanBusRx.Setup(RX_PIN, VAN_PACKET_QUEUE_SIZE); VanBusRx.SetDropPolicy(VAN_PACKET_QUEUE_SIZE * 8 / 10, &IsImportantPacket); ``` 以上示例将在接收队列包含 48 个或更多数据包时丢弃传入的数据包,除非它们被 ```IsImportantPacket``` 识别。 #### 10. ```bool 同步发送数据包(uint16_t iden, uint8_t cmdFlags, const uint8_t* data, size_t dataLen, unsigned int timeOutMs = 10)``` 发送一个数据包进行传输。如果数据包成功发送,则返回 ```true```。 #### 11. ```bool 发送数据包(uint16_t iden, uint8_t cmdFlags, const uint8_t* data, size_t dataLen, unsigned int timeOutMs = 10)``` 将一个数据包排队进行传输。如果数据包成功排队,则返回 ```true```。 #### 12. ```uint32_t 获取发送计数()``` 返回自开机以来提供用于发送的 VAN 数据包数量。计数器可能会溢出回绕。 ### VAN 数据包 在 VAN 总线上,电信号与 CAN 相同。然而,CAN 和 VAN 在线路上使用不同的数据编码,这使得在 VAN 总线上使用 CAN 接口成为不可能。 背景阅读: - [关于 VAN 的 Wiki 页面](https://en.wikipedia.org/wiki/Vehicle_Area_Network) - [VAN 线路协议 - Graham Auld - 2011 年 11 月 27 日](http://graham.auld.me.uk/projects/vanbus/lineprotocol.html) - [数据手册集合 - Graham Auld](http://graham.auld.me.uk/projects/vanbus/datasheets/) - [关于增强型曼彻斯特编码的讲座(法语) - Alain Chautar - 2003 年 6 月 30 日](http://www.educauto.org/files/file_fields/2013/11/18/mux1.pdf) - [关于 VAN 总线访问:冲突与仲裁的讲座(法语) - Alain Chautar - 2003 年 10 月 10 日](http://www.educauto.org/sites/www.educauto.org/files/file_fields/2013/11/18/mux2.pdf) - [关于帧格式的讲座(法语) - Alain Chautar - 2004 年 1 月 22 日](http://www.educauto.org/files/file_fields/2013/11/18/mux3.pdf) - [工业网络 CAN / VAN - 硕士课程 - Marie-Agnès Peraldi-Frati - 2008 年 1 月](http://www.i3s.unice.fr/~map/Cours/MASTER_STIC_SE/COURS32007.pdf) - [De CAN à CANopen en passant par VAN(法语) - Jean Mercklé - 2006 年 3 月](http://ebajic.free.fr/Ecole%20Printemps%20Reseau%20Mars%202006/Supports/J%20MERCKLE%20CANopen.pdf) - [Les réseaux VAN - CAN(法语) - Guerrin Guillaume, Guers Jérôme, Guinchard Sébastien - 2005 年 2 月](http://igm.univ-mlv.fr/~duris/NTREZO/20042005/Guerrin-Guers-Guinchard-VAN-CAN-rapport.pdf) - [Le bus VAN, vehicle area network: Fondements du protocole(法语) 平装本 – 1997 年 6 月 4 日](https://www.amazon.com/bus-VAN-vehicle-area-network/dp/2100031600) - [用于 Saleae USB 逻辑分析仪的车辆局域网 (VAN 总线) 分析仪 - Peter Pinter](https://github.com/morcibacsi/VanAnalyzer/) - [带串行接口的 Atmel TSS463C VAN 数据链路控制器](http://ww1.microchip.com/downloads/en/DeviceDoc/doc7601.pdf) - [用于 Xsara Picasso 和 Xsara 的多路复用 BSI 工作原理 - VAN 协议](https://web.archive.org/web/20221115101711/http://milajda22.sweb.cz/Manual_k_ridici_jednotce.pdf#page=17) 以下方法可用于通过 ```VanBusRx.Receive(...)``` 获取的 ```TVanPacketRxDesc``` 数据包对象: 1. [```uint16_t Iden()```](#iden) 2. [```uint8_t CommandFlags()```](#commandflags) 3. [```const uint8_t* Data()```](#data) 4. [```int DataLen()```](#datalen) 5. [```unsigned long Millis()```](#millis) 6. [```uint16_t Crc()```](#crc) 7. [```bool CheckCrc()```](#checkcrc) 8. [```bool CheckCrcAndRepair()```](#checkcrcandrepair) 9. [```void DumpRaw(Stream& s, char last = '\n')```](#dumpraw) 10. [```const char* CommandFlagsStr()```](#commandflagsstr) 11. [```const char* AckStr()```](#ackstr) 12. [```const char* ResultStr()```](#resultstr) 13. [```const TIfsDebugPacket& getIfsDebugPacket()```](#getifsdebugpacket) 14. [```const TIsrDebugPacket& getIsrDebugPacket()```](#getisrdebugpacket) #### 13. ```uint16_t 标识符()``` 返回 VAN 数据包的 IDEN 字段。 已知 IDEN 值的概述可以在以下位置找到,例如: - http://pinterpeti.hu/psavanbus/PSA-VAN.html - http://graham.auld.me.uk/projects/vanbus/protocol.html #### 14. ```uint8_t 命令标志()``` 返回 VAN 数据包的 4 位 "命令" FLAGS 字段。每个 VAN 数据包有 4 个 "命令" 标志: - EXT(位 3,最高有效位):始终为 1 - RAK(位 2):1 = 请求确认 - R/W(位 1):1 = 读操作,0 = 写操作 - RTR(位 0,最低有效位):1 = 远程传输请求 详细解释(法语)可在此处第 6 和 7 页找到: http://www.educauto.org/files/file_fields/2013/11/18/mux3.pdf#page=6 。 #### 15. ```const uint8_t* 数据()``` 返回 VAN 数据包的数据字段(字节)。 #### 16. ```int 数据长度()``` 返回 VAN 数据包中的数据字节数。一个 VAN 数据包中最多可以有 28 个数据字节。 #### 17. ```unsigned long 毫秒()``` 数据包时间戳,单位为毫秒。 #### 18. ```uint16_t Crc()``` 返回 VAN 数据包的 15 位 CRC 值。 #### 19. ```bool 检查Crc()``` 检查 VAN 数据包的 CRC 值。 #### 20. ```bool 检查并修复Crc()``` 检查 VAN 数据包的 CRC 值。如果不匹配,则尝试通过翻转每一位进行修复。如果数据包正常(无论是在修复前还是修复后),则返回 ```true```。 #### 21. ```void 导出原始数据(Stream& s, char last = '\n')``` 将原始数据包字节转储到流中。可选择指定最后一个字符;默认为 '\n'(换行符)。 调用示例: ``` pkt.DumpRaw(Serial); ``` 输出示例: ``` Raw: #0002 ( 2/15) 11(16) 0E 4D4 RA0 82-0C-01-00-11-00-3F-3F-3F-3F-82:7B-A4 ACK OK 7BA4 CRC_OK ``` 转储到字符数组的示例: ``` const char* PacketRawToStr(TVanPacketRxDesc& pkt) { static char dumpBuffer[MAX_DUMP_RAW_SIZE]; GString str(dumpBuffer); PrintAdapter streamer(str); pkt.DumpRaw(streamer, '\0'); return dumpBuffer; } ``` 注意:为此,您需要安装 [PrintEx](https://github.com/Chris--A/PrintEx) 库。我测试过 版本 1.2.0 。 #### 22. ```const char* 命令标志字符串()``` 将 VAN 数据包的 "命令" FLAGS 字段作为字符串返回。 注意:使用静态分配的缓冲区,因此不要在同一个 printf 调用中两次调用此方法。 #### 23. ```const char* 确认字符串()``` 将 VAN 数据包的 ACK 字段作为字符串返回,可以是 "ACK" 或 "NO_ACK"。 #### 24. ```const char* 结果字符串()``` 将 VAN 数据包的 RESULT 字段作为字符串返回,可以是 "OK" 或以 "ERROR_" 开头的字符串。 #### 25. ```const TIfsDebugPacket& 获取Ifs调试数据包()``` 检索可用于分析帧间间隔事件的调试结构。 仅在 ```#define VAN_RX_ISR_DEBUGGING``` 未被注释时可用(参见 [```VanBusRx.h```](https://github.com/0xCAFEDECAF/VanBus/blob/756b05097e57c183f87b7879e431308daef5ce5f/VanBusRx.h#L32))。 #### 26. ```const TIsrDebugPacket& 获取Isr调试数据包()``` 检索可用于分析(观察到的)位时序的调试结构。 仅在 ```#define VAN_RX_IFS_DEBUGGING``` 未被注释时可用(参见 [```VanBusRx.h```](https://github.com/0xCAFEDECAF/VanBus/blob/756b05097e57c183f87b7879e431308daef5ce5f/VanBusRx.h#L33))。 ## 👷 待完成工作 ### 未来 目前该库仅支持 125 kbit/s 的 VAN 总线。需要添加对不同速率的支持,如 62.5 kbit/s,这可以作为可选参数传递给 ```VanBusRx.Setup(...)```。 ## 📖 许可证 此库是开源的,并根据 [MIT 许可证](http://opensource.org/licenses/MIT) 授权。 您可以随心所欲地使用它,但欢迎贡献! ## 另请参阅 - [VAN 实时连接](https://github.com/0xCAFEDECAF/VanLiveConnect) - 您 PSA 车辆(标致、雪铁龙)的实时数据直接来自 VAN 总线,显示在您的智能手机或平板电脑上。 - [ESP32 RMT 外设车辆局域网 (VAN 总线) 读取器]
标签:Arduino, ESP32, ESP8266, PSA车辆, VAN总线, 传感器网络, 嵌入式系统, 开源硬件, 总线协议, 数据包读写, 标致雪铁龙, 汽车电子, 物联网, 车载网络, 车载诊断, 车辆通信