PSA (Peugeot, Citroën) VAN bus reader/writer for Arduino-ESP8266
Reading and writing packets from/to PSA vehicles' VAN bus.
📝 Table of Contents
🎈 Description
This module allows you to receive and transmit packets on the "VAN" bus of your Peugeot or Citroen vehicle.
VAN bus is pretty similar to CAN bus. It was used in many cars (Peugeot, Citroen) made by PSA.
In the beginning of 2000's the PSA group (Peugeot and Citroen) used VAN bus as a communication protocol between the various comfort-related equipment. Later, around 2005, they started to replace this protocol in their newer cars with the CAN bus protocol, however some models had VAN bus inside them until 2009.
Only ESP8266 / ESP8285 is supported. ESP32 is NOT supported by this library; for boards with those MCUs there is this excellent library: ESP32 RMT peripheral Vehicle Area Network (VAN bus) reader.
🔌 Schematics
You can usually find the VAN bus on pins 2 and 3 of ISO block "A" of your head unit (car radio). See https://en.wikipedia.org/wiki/Connectors_for_car_audio and https://github.com/morcibacsi/esp32_rmt_van_rx#schematics .
There are various possibilities to hook up a ESP8266 based board to your vehicle's VAN bus:
- Use a MCP2551 transceiver, connected with its CANH and CANL pins to the vehicle's VAN bus.
As the MCP2551 has 5V logic, a 5V
↔ 3.3V level converter is needed to connect the CRX / RXD / R pin of the transceiver, via the level converter, to a GPIO pin of your ESP8266 board. For transmitting packets, also connect the CTX / TXD / D pins of the transceiver, via the level converter, to a GPIO pin of your ESP8266 board.
👉 Note: CANH of the transceiver is connected to VAN BAR, CANL to VAN. This may seem illogical but in practice it turns out this works best.
- Use a SN65HVD230 transceiver, connected with its CANH and CANL pins to the vehicle's VAN bus. The SN65HVD230 transceiver already has 3.3V logic, so it is possible to directly connect the CRX / RXD / R pin of the transceiver to a GPIO pin of your ESP8266 board. For transmitting packets, also connect the CTX / TXD / D pins of the transceiver to a GPIO pin of your ESP8266 board.
👉 Note: CANH of the transceiver is connected to VAN BAR, CANL to VAN. This may seem illogical but in practice it turns out this works best.
- The simplest schematic is not to use a transceiver at all, but connect the VAN DATA line to GrouND using two 4.7 kOhm resistors. Connect the GPIO pin of your ESP8266 board to the 1:2 voltage divider that is thus formed by the two resistors. This is only for receiving packets, not for transmitting. Results may vary.
👉 Note: I used this schematic during many long debugging hours, but I cannot guarantee that it won't ultimately cause your car to explode! (or anything less catastrofic)
🚀 Usage
General
Add the following line to your .ino
sketch:
#include <VanBus.h>
For receiving and transmitting packets:
- Add the following lines to your initialisation block
void setup()
:
int TX_PIN = D3; // Set to GPIO pin connected to VAN bus transceiver input
int RX_PIN = D2; // Set to GPIO pin connected to VAN bus transceiver output
TVanBus::Setup(RX_PIN, TX_PIN);
- Add e.g. the following lines to your main loop
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));
If you only want to receive packets, you may save a few hundred precious bytes by using directly the VanBusRx
object:
- Add the following lines to your initialisation block
void setup()
:
int RX_PIN = D2; // Set to GPIO pin connected to VAN bus transceiver output
VanBusRx.Setup(RX_PIN);
- Add the following lines to your main loop
void loop()
:
TVanPacketRxDesc pkt;
if (VanBusRx.Receive(pkt)) pkt.DumpRaw(Serial);
VanBus
object
The following methods are available for the VanBus
object:
Interfaces for both receiving and transmitting of packets:
Interfaces for receiving packets:
bool Available()
bool Receive(TVanPacketRxDesc& pkt, bool* isQueueOverrun = NULL)
uint32_t GetRxCount()
Interfaces for transmitting packets:
bool SyncSendPacket(uint16_t iden, uint8_t cmdFlags, const uint8_t* data, size_t dataLen, unsigned int timeOutMs = 10)
bool SendPacket(uint16_t iden, uint8_t cmdFlags, const uint8_t* data, size_t dataLen, unsigned int timeOutMs = 10)
uint32_t GetTxCount()
void Setup(uint8_t rxPin, uint8_t txPin)
1. Start the receiver listening on GPIO pin rxPin
. The transmitter will transmit on GPIO pin txPin
.
void DumpStats(Stream& s)
2. Dumps a few packet statistics on the passed stream.
bool Available()
3. Returns true
if a VAN packet is available in the receive queue.
bool Receive(TVanPacketRxDesc& pkt, bool* isQueueOverrun = NULL)
4. Copy a VAN packet out of the receive queue, if available. Otherwise, returns false
.
If a valid pointer is passed to 'isQueueOverrun', will report then clear any queue overrun condition.
uint32_t GetRxCount()
5. Returns the number of received VAN packets since power-on. Counter may roll over.
bool SyncSendPacket(uint16_t iden, uint8_t cmdFlags, const uint8_t* data, size_t dataLen, unsigned int timeOutMs = 10)
6. Sends a packet for transmission. Returns true
if the packet was successfully transmitted.
bool SendPacket(uint16_t iden, uint8_t cmdFlags, const uint8_t* data, size_t dataLen, unsigned int timeOutMs = 10)
7. Queues a packet for transmission. Returns true
if the packet was successfully queued.
uint32_t GetTxCount()
8. Returns the number of VAN packets, offered for transmitting, since power-on. Counter may roll over.
VAN packets
On a VAN bus, the electrical signals are the same as CAN. However, CAN and VAN use different data encodings on the line which makes it impossible to use CAN interfaces on a VAN bus.
For background reading:
- Wiki page on VAN
- VAN line protocol - Graham Auld - November 27, 2011
- Collection of data sheets - Graham Auld
- Lecture on Enhanced Manchester Coding (in French) - Alain Chautar - June 30, 2003
- Lecture on VAN bus access: collisions and arbitration (in French) - Alain Chautar - October 10, 2003
- Lecture on frame format (in French) - Alain Chautar - January 22, 2004
- Industrial networks CAN / VAN - Master Course - Marie-Agnès Peraldi-Frati - January 2008
- De CAN à CANopen en passant par VAN (in French) - Jean Mercklé - March 2006
- Les réseaux VAN - CAN (in French) - Guerrin Guillaume, Guers Jérôme, Guinchard Sébastien - February 2005
- Le bus VAN, vehicle area network: Fondements du protocole (French) Paperback – June 4, 1997
- Vehicle Area Network (VAN bus) Analyzer for Saleae USB logic analyzer - Peter Pinter
- Atmel TSS463C VAN Data Link Controller with Serial Interface
- Multiplexed BSI Operating Principle for the Xsara Picasso And Xsara - The VAN protocol
The following methods are available for TVanPacketRxDesc
packet objects as obtained from
VanBusRx.Receive(...)
:
uint16_t Iden()
uint16_t CommandFlags()
const uint8_t* Data()
int DataLen()
uint16_t Crc()
bool CheckCrc()
bool CheckCrcAndRepair()
void DumpRaw(Stream& s, char last = '\n')
const TIsrDebugPacket& getIsrDebugPacket()
const char* CommandFlagsStr()
const char* AckStr()
const char* ResultStr()
uint16_t Iden()
1. Returns the IDEN field of the VAN packet.
An overview of known IDEN values can be found e.g. at:
uint16_t CommandFlags()
2. Returns the "command" FLAGS field of the VAN packet. Each VAN packet has 4 "command" flags:
- EXT : always 1
- RAK : 1 = Requesting AcKnowledge
- R/W : 1 = Read operation, 0 = Write operation
- RTR : 1 = Remote Transmit Request
A thorough explanation is found (in French) on page 6 and 7 of http://www.educauto.org/files/file_fields/2013/11/18/mux3.pdf#page=6 .
const uint8_t* Data()
3. Returns the data field (bytes) of the VAN packet.
int DataLen()
4. Returns the number of data bytes in the VAN packet. There can be at most 28 data bytes in a VAN packet.
unsigned long Millis()
5. Packet time stamp in milliseconds.
uint16_t Crc()
6. Returns the 15-bit CRC value of the VAN packet.
bool CheckCrc()
7. Checks the CRC value of the VAN packet.
bool CheckCrcAndRepair()
8. Checks the CRC value of the VAN packet. If not, tries to repair it by flipping each bit. Returns true
if the
packet is OK (either before or after the repair).
void DumpRaw(Stream& s, char last = '\n')
9. Dumps the raw packet bytes to a stream. Optionally specify the last character; default is '\n' (newline).
Example of invocation:
pkt.DumpRaw(Serial);
Example of output:
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
Example of dumping into a char array:
const char* PacketRawToStr(TVanPacketRxDesc& pkt)
{
static char dumpBuffer[MAX_DUMP_RAW_SIZE];
GString str(dumpBuffer);
PrintAdapter streamer(str);
pkt.DumpRaw(streamer, '\0');
return dumpBuffer;
}
Note: for this, you will need to install the PrintEx library. I tested with version 1.2.0 .
const TIsrDebugPacket& getIsrDebugPacket()
10. Retrieves a debug structure that can be used to analyse (observed) bit timings.
const char* CommandFlagsStr()
11. Returns the "command" FLAGS field of the VAN packet as a string
Note: uses a statically allocated buffer, so don't call this method twice within the same printf invocation.
const char* AckStr()
12. Returns the ACK field of the VAN packet as a string, either "ACK" or "NO_ACK".
const char* ResultStr()
13. Returns the RESULT field of the VAN packet as a string, either "OK" or a string starting with "ERROR_".
⚠️ Limitations, Caveats
The library times the incoming bits using an interrupt service routine (ISR) that triggers on pin "change"
events (see the internal function RxPinChangeIsr
in
VanBusRx.cpp).
When Wi-Fi is enabled, the invocation of the ISR is often quite late (even if there is no Wi-Fi connection active). It turns out that disabling Wi-Fi altogether is the way to get a completely error-free packet reception, like this:
15:10:26.335 -> received pkts: 104883, corrupt: 0 (0.000%), repaired: 0 (---%), overall: 0 (0.000%)
With Wi-Fi enabled, not all VAN bus packets are received error-free. Even after trying to "repair" a packet
(see function bool CheckCrcAndRepair()
), the long-term average packet loss is around
0.01% ... 0.015% (between 1 in 10,000 and 1 in 6,666), which is pretty workable for most applications. I am
investigating how to improve on this.
👷 Work to be Done
Near future
Currently the library supports only 125 kbit/s VAN bus. Need to add support for different rate, like 62.5 kbit/s,
which can be passed as an optional parameter to VanBusRx.Setup(...)
.
📖 License
This library is open-source and licensed under the MIT license.
Do whatever you like with it, but contributions are appreciated!
See also
- VAN Live Connect - Live data from your PSA vehicle (Peugeot, Citroën) on your smartphone or tablet, directly from the VAN bus.