J2534 / CANbus Protocol Reference
Targeting: Adafruit Feather M4 CAN Express (SAME51) — for building a Pass-Thru style read/write .bin interface.
1. Hardware — Feather M4 CAN Express
Specs
- MCU: ATSAME51J19A — 120 MHz Cortex-M4F
- Flash: 512 KB
- RAM: 192 KB
- CAN Controller: Built-in (SAME51 native CAN peripheral)
- Transceiver: On-board CAN transceiver + 5V boost
- Connector: 3-pin 3.5mm terminal block (CANH, CANL, GND)
- Adafruit Product: #4759
Pin Setup
PIN_CAN_STANDBY → set LOW (wake transceiver)
PIN_CAN_BOOSTEN → set HIGH (enable 5V boost)
- Wire: H↔H, L↔L between nodes
- GND connection optional but recommended
- 120Ω termination resistor at each end of bus
2. CAN Bus Basics
| Property | Value |
| Physical Layer | Differential pair (CANH / CANL), ISO 11898 |
| Standard ID | 11-bit (CAN 2.0A) |
| Extended ID | 29-bit (CAN 2.0B) |
| Max Data Length | 8 bytes per frame (classic CAN) |
| Common Baud Rates | 125, 250, 500 kbps, 1 Mbps |
| Bus Arbitration | Non-destructive, priority by ID (lower wins) |
| Termination | 120Ω at each end of bus |
CAN Frame Structure
┌─────────┬────────┬─────┬───────────────┬─────┬─────┬─────────┐
│ SOF │ ID │ RTR │ DLC (0-8) │DATA │ CRC │ ACK/EOF │
│ 1 bit │11/29b │1 bit│ 4 bits │0-8B │15b │ │
└─────────┴────────┴─────┴───────────────┴─────┴─────┴─────────┘
3. Arduino CAN Setup & Code
Library
Install "Adafruit CAN" (CANSAME5x) via Arduino Library Manager. Board selection: Feather M4 CAN (SAME51).
Sender Example
#include <CANSAME5x.h>
CANSAME5x CAN;
void setup() {
Serial.begin(115200);
while (!Serial) delay(10);
pinMode(PIN_CAN_STANDBY, OUTPUT);
digitalWrite(PIN_CAN_STANDBY, false); // transceiver out of standby
pinMode(PIN_CAN_BOOSTEN, OUTPUT);
digitalWrite(PIN_CAN_BOOSTEN, true); // enable 5V boost
if (!CAN.begin(250000)) { // 250 kbps — must match all nodes
Serial.println("CAN init failed!");
while (1) delay(10);
}
Serial.println("CAN TX ready");
}
void loop() {
// Send a standard 11-bit ID packet
CAN.beginPacket(0x12); // CAN ID = 0x12
CAN.write('H');
CAN.write('i');
CAN.endPacket();
delay(100);
}
Receiver Example
#include <CANSAME5x.h>
CANSAME5x CAN;
void setup() {
Serial.begin(115200);
while (!Serial) delay(10);
pinMode(PIN_CAN_STANDBY, OUTPUT);
digitalWrite(PIN_CAN_STANDBY, false);
pinMode(PIN_CAN_BOOSTEN, OUTPUT);
digitalWrite(PIN_CAN_BOOSTEN, true);
if (!CAN.begin(250000)) {
Serial.println("CAN init failed!");
while (1) delay(10);
}
Serial.println("CAN RX ready");
}
void loop() {
int packetSize = CAN.parsePacket();
if (packetSize) {
Serial.print("ID: 0x");
Serial.print(CAN.packetId(), HEX);
Serial.print(" DLC: ");
Serial.print(packetSize);
Serial.print(" Data: ");
while (CAN.available()) {
Serial.print((char)CAN.read());
}
Serial.println();
}
}
Important: All nodes on the bus must use the same baud rate. 250 kbps is the recommended default for the Feather M4 CAN.
4. J2534 Pass-Thru Protocol Overview
SAE J2534 (Pass-Thru) is a standardized API that lets a PC application communicate with vehicle ECUs through a compliant hardware interface. Originally mandated for emissions-related ECU reprogramming, it now covers general diagnostics.
Key Concepts
- Pass-Thru Device: Hardware bridge between PC (USB) and vehicle (OBD-II port)
- DLL API: Windows DLL exporting standardized C functions; OEM software calls these
- Protocol-Agnostic: One device, one API — works across CAN, ISO9141, KWP2000, J1850, etc.
- Channel: A logical connection to a specific protocol on the device
- Filter: Hardware-level message filtering (pass/block) to reduce traffic to the PC
Architecture
┌──────────────┐ USB ┌─────────────────┐ OBD-II ┌───────┐
│ PC / OEM │ ◄──────────► │ J2534 Device │ ◄──────────► │ ECU │
│ Application │ J2534 DLL │ (Pass-Thru HW) │ CAN bus │ │
└──────────────┘ └─────────────────┘ └───────┘
5. J2534 API Functions
| Function | Purpose |
PassThruOpen | Open connection to J2534 device; returns DeviceID |
PassThruClose | Close connection to device |
PassThruConnect | Open a protocol channel (CAN, ISO15765, etc.); returns ChannelID |
PassThruDisconnect | Close protocol channel |
PassThruReadMsgs | Read messages from receive buffer |
PassThruWriteMsgs | Send messages to vehicle network |
PassThruStartMsgFilter | Set pass/block/flow-control filter |
PassThruStopMsgFilter | Remove a message filter |
PassThruStartPeriodicMsg | Queue a message for periodic transmission |
PassThruStopPeriodicMsg | Stop a periodic message |
PassThruIoctl | Get/set config params, clear buffers, init protocols |
PassThruSetProgrammingVoltage | Set voltage on pin 15 (for flash programming) |
PassThruReadVersion | Read firmware/DLL/API version strings |
PassThruGetLastError | Get human-readable error description |
Function Prototypes
long PassThruOpen(void *pName, unsigned long *pDeviceID);
long PassThruClose(unsigned long DeviceID);
long PassThruConnect(unsigned long DeviceID, unsigned long ProtocolID,
unsigned long Flags, unsigned long BaudRate,
unsigned long *pChannelID);
long PassThruDisconnect(unsigned long ChannelID);
long PassThruReadMsgs(unsigned long ChannelID, PASSTHRU_MSG *pMsg,
unsigned long *pNumMsgs, unsigned long Timeout);
long PassThruWriteMsgs(unsigned long ChannelID, PASSTHRU_MSG *pMsg,
unsigned long *pNumMsgs, unsigned long Timeout);
long PassThruStartMsgFilter(unsigned long ChannelID, unsigned long FilterType,
PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg,
PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID);
long PassThruIoctl(unsigned long ChannelID, unsigned long IoctlID,
void *pInput, void *pOutput);
6. PASSTHRU_MSG Structure
typedef struct {
unsigned long ProtocolID; // Protocol for this message
unsigned long RxStatus; // Receive status flags
unsigned long TxFlags; // Transmit option flags
unsigned long Timestamp; // Timestamp in microseconds
unsigned long DataSize; // Number of bytes in Data[]
unsigned long ExtraDataIndex; // Start of extra data (e.g. CRC)
unsigned char Data[4128]; // Message payload
} PASSTHRU_MSG;
Protocol IDs
| ID | Protocol | Notes |
| 0x01 | J1850VPW | Variable Pulse Width |
| 0x02 | J1850PWM | Pulse Width Modulation |
| 0x03 | ISO9141 | Slow init K-line |
| 0x04 | ISO14230 (KWP2000) | K-line fast/slow init |
| 0x05 | CAN | Raw CAN frames |
| 0x06 | ISO15765 | CAN transport layer (needed for UDS/flash) |
TX Flags
| Flag | Value | Meaning |
| CAN_29BIT_ID | 0x00000100 | Use 29-bit extended CAN ID |
| ISO15765_FRAME_PAD | 0x00000040 | Pad ISO15765 frames to 8 bytes |
| WAIT_P3_MIN_ONLY | 0x00000200 | Override P3 timing |
RX Status Flags
| Flag | Value | Meaning |
| TX_MSG_TYPE | 0x0001 | Echo of transmitted message |
| START_OF_MESSAGE | 0x0002 | First-frame indication |
| RX_BREAK | 0x0004 | Break received |
| TX_INDICATION | 0x0008 | Tx complete indication |
| ISO15765_PADDING_ERROR | 0x0010 | Padding error detected |
| ISO15765_ADDR_TYPE | 0x0080 | Extended addressing |
7. Error Codes
| Code | Name | Meaning |
| 0x00 | STATUS_NOERROR | Success |
| 0x01 | ERR_NOT_SUPPORTED | Function not supported by device |
| 0x02 | ERR_INVALID_CHANNEL_ID | Bad channel identifier |
| 0x03 | ERR_INVALID_PROTOCOL_ID | Unrecognized protocol |
| 0x04 | ERR_NULL_PARAMETER | Null pointer passed |
| 0x05 | ERR_INVALID_IOCTL_VALUE | Invalid config parameter |
| 0x06 | ERR_INVALID_FLAGS | Invalid flags in message |
| 0x07 | ERR_FAILED | Unspecified error |
| 0x08 | ERR_DEVICE_NOT_CONNECTED | Device disconnected |
| 0x09 | ERR_TIMEOUT | Timeout expired |
| 0x0A | ERR_INVALID_MSG | Message violates constraints |
| 0x0B | ERR_INVALID_TIME_INTERVAL | Time value out of range |
| 0x0C | ERR_EXCEEDED_LIMIT | Filter/periodic limit exceeded |
| 0x0D | ERR_INVALID_MSG_ID | Unknown message handle |
| 0x0E | ERR_DEVICE_IN_USE | Device already opened |
| 0x0F | ERR_INVALID_IOCTL_ID | Unknown ioctl command |
| 0x10 | ERR_BUFFER_EMPTY | No messages to read |
| 0x11 | ERR_BUFFER_FULL | Transmit queue full |
| 0x12 | ERR_BUFFER_OVERFLOW | Receive buffer overflow |
| 0x13 | ERR_PIN_INVALID | Unknown or in-use pin |
| 0x14 | ERR_CHANNEL_IN_USE | Protocol channel occupied |
| 0x15 | ERR_MSG_PROTOCOL_ID | Message protocol mismatch |
| 0x16 | ERR_INVALID_FILTER_ID | Unknown filter handle |
| 0x17 | ERR_NO_FLOW_CONTROL | Missing flow control filter |
| 0x18 | ERR_NOT_UNIQUE | Duplicate filter pattern |
| 0x19 | ERR_INVALID_BAUDRATE | Requested baud rate not achievable |
| 0x1A | ERR_INVALID_DEVICE_ID | Unknown device handle |
8. Supported Protocols & Baud Rates
J2534-1 (Mandatory)
| Protocol | Baud Rates |
| CAN (ISO 11898) | 125, 250, 500 kbps; 11-bit & 29-bit IDs |
| ISO 15765 (CAN transport) | Same as CAN — adds segmentation & flow control |
| ISO 9141-2 | 10400 baud (±0.5%), plus 4800–19200 range |
| ISO 14230-4 (KWP2000) | Same range as ISO 9141 |
| J1850 VPW | 10.4 kbps (high-speed 41.6 kbps) |
| J1850 PWM | 41.6 kbps (high-speed 83.3 kbps) |
| SAE J2610 | DaimlerChrysler SCI |
J2534-2 (Optional Extensions)
- SAE J1939 (heavy-duty CAN)
- SAE J1708 (serial)
- Single Wire CAN (GMLAN)
- Ford Medium-Speed CAN (MS-CAN)
- GM UART
- Honda Diag-H
- TP 2.0 (VW/Audi)
- Fault Tolerant CAN
For M4 Feather CAN: The SAME51 supports classic CAN at 62.5, 125, 250, 500 kbps and 1 Mbps.
OBD-II / automotive standard is typically 500 kbps for CAN, though some vehicles use 250 kbps.
9. UDS Services for Read/Write .bin Files
UDS (Unified Diagnostic Services — ISO 14229) runs on top of ISO 15765 (CAN transport layer). These are the services you need for reading/writing binary firmware to/from ECUs.
Session & Security (Required Before Flashing)
| SID | Service | Purpose |
| 0x10 | DiagnosticSessionControl | Switch to Programming Session (sub-function 0x02) |
| 0x27 | SecurityAccess | Seed/Key authentication to unlock ECU |
| 0x3E | TesterPresent | Keep session alive (send periodically) |
| 0x11 | ECUReset | Reset ECU after flashing |
Upload / Download Services (The Core for .bin R/W)
| SID | Service | Direction | Purpose |
| 0x34 | RequestDownload | PC → ECU | Initiate writing .bin data TO the ECU. Specifies memory address & size. |
| 0x35 | RequestUpload | ECU → PC | Initiate reading .bin data FROM the ECU. Specifies memory address & size. |
| 0x36 | TransferData | Both | Transfer data blocks (chunked). Block counter increments each call. |
| 0x37 | RequestTransferExit | — | Signal transfer complete. ECU verifies integrity. |
Memory Erase (Often Needed Before Write)
| SID | Service | Purpose |
| 0x31 | RoutineControl | Start/stop routines — commonly used to erase flash memory before writing |
UDS Negative Response Codes (Flash-Related)
| NRC | Name | Meaning |
| 0x13 | incorrectMessageLengthOrInvalidFormat | Bad request format |
| 0x31 | requestOutOfRange | Address/size outside valid range |
| 0x33 | securityAccessDenied | Not authenticated |
| 0x72 | generalProgrammingFailure | Flash erase/program failed |
| 0x73 | wrongBlockSequenceCounter | TransferData block number mismatch |
| 0x78 | requestCorrectlyReceivedResponsePending | ECU busy, wait for response |
10. Connection & Flash Flow
J2534 Initialization Sequence
1. PassThruOpen() → get DeviceID
2. PassThruConnect(DeviceID,
ISO15765, // ProtocolID = 0x06
0, // Flags (0 = 11-bit CAN ID)
500000, // Baud rate
&channelID) → get ChannelID
3. PassThruStartMsgFilter(channelID,
FLOW_CONTROL_FILTER, // FilterType
&maskMsg, // Mask: which bits to check
&patternMsg, // Pattern: ECU response ID (e.g. 0x7E8)
&flowControlMsg, // Flow control: tester ID (e.g. 0x7E0)
&filterID)
4. PassThruIoctl(SET_CONFIG, ...) → set timing params (P1–P4, etc.)
Write .bin to ECU (Download)
1. UDS 0x10 0x02 → Enter Programming Session
2. UDS 0x27 0x01 → SecurityAccess: request seed
3. UDS 0x27 0x02 [key] → SecurityAccess: send key
4. UDS 0x31 [erase] → RoutineControl: erase flash region
5. UDS 0x34 [addr][size] → RequestDownload: specify address & total size
Response contains max block length
6. Loop:
UDS 0x36 [blockN][data]→ TransferData: send .bin in chunks
7. UDS 0x37 → RequestTransferExit
8. UDS 0x11 0x01 → ECUReset (hard reset)
Read .bin from ECU (Upload)
1. UDS 0x10 0x02 → Enter Programming Session
2. UDS 0x27 0x01/0x02 → SecurityAccess (seed/key)
3. UDS 0x35 [addr][size] → RequestUpload: specify address & size
Response contains max block length
4. Loop:
UDS 0x36 [blockN] → TransferData: receive .bin chunks
Save chunks to file
5. UDS 0x37 → RequestTransferExit
6. UDS 0x11 0x01 → ECUReset
11. Mapping J2534 API → M4 Feather CAN
The Feather M4 CAN isn't a J2534 device, but you can implement the equivalent functionality on it. Here's the mapping:
| J2534 Concept | M4 Feather Equivalent |
PassThruOpen | CAN.begin(baudRate) + pin setup |
PassThruConnect (ISO15765) | Use CAN.begin() + implement ISO-TP framing in software |
PassThruReadMsgs | CAN.parsePacket() + CAN.read() |
PassThruWriteMsgs | CAN.beginPacket(id) / CAN.write() / CAN.endPacket() |
PassThruStartMsgFilter | Software filter on CAN.packetId(), or use SAME51 hardware filters |
PassThruIoctl | Timing managed in firmware (millis/micros) |
| ISO 15765 transport layer | Must implement manually: Single Frame / First Frame / Consecutive Frame / Flow Control |
| UDS service layer | Must implement manually: Build/parse UDS request/response bytes |
| PC ↔ Feather link | USB Serial — send commands / receive data from a PC-side script |
Key requirement for .bin read/write: You need to implement ISO 15765-2 (ISO-TP) on top of raw CAN.
This handles segmentation of messages larger than 8 bytes — essential since UDS flash transfers
send/receive blocks of up to ~4 KB at a time. The CANSAME5x library gives you raw CAN frames only.
ISO-TP Frame Types (ISO 15765-2)
| Type | Nibble | Purpose |
| Single Frame (SF) | 0x0 | Message fits in one CAN frame (≤7 bytes) |
| First Frame (FF) | 0x1 | First segment of multi-frame message; contains total length |
| Consecutive Frame (CF) | 0x2 | Subsequent segments; sequence number 0–F |
| Flow Control (FC) | 0x3 | Receiver tells sender: continue / wait / abort |
Minimal Implementation Checklist
- CAN driver init (CANSAME5x at 500 kbps for OBD-II)
- ISO-TP layer: assemble/disassemble multi-frame messages
- UDS request builder: construct SID + sub-function + data bytes
- UDS response parser: handle positive responses, NRCs, and 0x78 pending
- Serial command interface: PC sends commands like
READ addr size / WRITE addr file.bin
- File transfer over serial: chunked binary transfer between PC and Feather
12. Sources
Reference compiled March 2026. SAE J2534 v04.04 specification. For official details, obtain the full SAE standard document.