Is MCP2515 CAN Bus Transceiver Module Compatible with Home Assistant (ESPHome)?
Electrical Specifications
Always confirm pin assignments against the manufacturer datasheet before wiring.
Compatibility Overview
High-speed CAN bus communication interface for automotive OBD-II diagnostics solar inverter bus monitoring and industrial automation.
Wiring MCP2515 CAN Bus Transceiver Module to ESP32
| Module pin | ESP32 | Note |
|---|---|---|
| VCC | 5V | The TJA1050 transceiver needs at least 4.75V. See the first gotcha for what that does to the SPI lines. |
| GND | GND | |
| CS | GPIO5 | |
| SCK | GPIO18 | |
| SI | GPIO23 | MOSI. |
| SO | GPIO19 | MISO — and on the stock module a 5V output driving a 3.3V input. |
| INT | unconnected | ESPHome polls the controller. |
| CANH / CANL | the bus | Twisted pair. On an OBD-II port, pins 6 and 14. |
| J1 | jumper | The 120Ω terminator. Fitted at the two physical ends of a bus and nowhere else. |
Host pins are the ones the configuration on this page uses. Move a pin here and change it in the YAML — they are documented together so they cannot disagree.
Technical Specification Matrix
| Feature / Attribute | Specification / Status |
|---|---|
| Hardware Protocol | SPI / 3.3V |
| Bridge Required | NO |
| Border Router Needed | None |
| Hardware Capabilities & Peripherals | CAN Packet Receiver OBD-II Telemetry Bus State Note: these entities come from the ESPHome configuration you flash — each one needs its matching component in that YAML before it appears. |
| Pairing Complexity | Advanced |
| Factory Reset Instruction | Hold BOOT button for 3 seconds. |
Wire, Flash, Adopt, Check
Wire it up
Follow the wiring table above. The ESP32 pins there are the ones the configuration uses, so if you move one, change it in both places.
Flash the configuration
Create a new device in the ESPHome dashboard (the Home Assistant add-on) and paste the configuration below into it, or save it as emb_esp32_mcp2515_ha.yaml and run esphome run emb_esp32_mcp2515_ha.yaml. The first flash goes over USB. If it is not detected, hold BOOT button for 3 seconds to put it in download mode. Every update after that is over the air.
Adopt it in Home Assistant
Settings → Devices & Services. A node on the same network shows up as a discovered ESPHome device within a minute of joining Wi-Fi; choose Configure and enter the API encryption key from your secrets.yaml. If discovery does not find it, add the ESPHome integration by hand with the node's IP address.
Check it works
The setup log reports the controller initialised. On a car with the engine running, pressing Request Engine RPM updates Engine RPM within a second. A controller that fails to initialise is SPI (or the SO voltage); one that initialises and never sees a frame is the clock setting or the terminator.
ESPHome Configuration
Starting configuration for MCP2515 CAN Bus Transceiver Module on ESP32. Untested on hardware — verify wiring and pins against your own board.
# ESPHome configuration for MCP2515 CAN Bus Transceiver Module
# Host: ESP32. Pin numbers below are host-side defaults — any free
# GPIO will do, and you should change them to match your own wiring.
esphome:
name: emb_esp32_mcp2515_ha
friendly_name: "MCP2515 CAN Bus Transceiver Module"
esp32:
board: esp32dev
framework:
type: esp-idf
logger:
api:
encryption:
key: !secret api_encryption_key
ota:
- platform: esphome
password: !secret ota_password
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
spi:
clk_pin: GPIO18
mosi_pin: GPIO23
miso_pin: GPIO19
# clock must match the crystal on the module — the figure printed on the
# metal can. Most of the cheap blue boards carry 8.000 MHz; some carry
# 16.000 MHz. A wrong value gives a bit rate that is out by 2x and a bus
# that never synchronises.
canbus:
- platform: mcp2515
id: can_bus
cs_pin: GPIO5
can_id: 4
bit_rate: 500KBPS
clock: 8MHZ
on_frame:
# 0x7E8 is the engine ECU's reply address on an OBD-II port.
# Mode 01 PID 0x0C is engine RPM: (A * 256 + B) / 4.
- can_id: 0x7E8
then:
- lambda: |-
if (x.size() >= 5 && x[1] == 0x41 && x[2] == 0x0C) {
id(engine_rpm).publish_state(((x[3] << 8) | x[4]) / 4.0);
}
sensor:
- platform: template
name: "Engine RPM"
id: engine_rpm
unit_of_measurement: "rpm"
accuracy_decimals: 0
# 0x7DF is the OBD-II broadcast request address.
button:
- platform: template
name: "Request Engine RPM"
on_press:
then:
- canbus.send:
canbus_id: can_bus
can_id: 0x7DF
data: [0x02, 0x01, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00]
Before you flash:
the !secret values come from your
secrets.yaml, and the GPIO numbers are host-side
defaults rather than properties of this part — change them to match how you actually wired it.
What Appears in Home Assistant
Decoded by the example lambda from the reply to OBD-II PID 0x0C. Every other value needs its own on_frame handler and its own template sensor.
Sends the mode 01 request to the broadcast address. Put the same canbus.send in an interval: to poll.
Known Problems and Fixes
The 5V/3.3V problem on the cheap module
The common blue board powers the MCP2515 and the TJA1050 from one VCC pin. The transceiver needs 5V, so the controller runs at 5V too, and its SO pin then drives 5V into the ESP32's MISO — above the 3.6V maximum. Three fixes: the trace-cut mod that feeds the MCP2515 3.3V and only the TJA1050 5V; a level shifter or resistor divider on SO; or a module sold with separate 3.3V and 5V pins.
clock is the crystal on the board
The MCP2515 derives its bit timing from the crystal next to it. The frequency is printed on the can — 8.000 on most of these modules, 16.000 on some — and clock: must match. A mismatch gives a bit rate out by two and a bus that never synchronises, with no error to say why.
Termination
A bench bus of two nodes wants both terminated. A car is already terminated at both ends, so a module plugged into the OBD port should have J1 removed — a third terminator loads the bus.
OBD-II basics
Most cars since 2008 use 500kbps with 11-bit IDs. Requests go to 0x7DF; the engine ECU replies from 0x7E8, other ECUs from 0x7E9 to 0x7EF. Some makes use 29-bit IDs (use_extended_id: true and different addresses). The config's decoder handles the RPM PID; the formulas for the others are public.
Frames are not entities
Nothing appears in Home Assistant until a lambda publishes it. A busy bus also delivers thousands of frames a second, more than SPI polling keeps up with; filter with can_id and can_id_mask on each on_frame handler so the node only decodes what it needs.
Report a Pinout Error or Submit Tested Code Snippets
Found a pin mapping quirk or built a custom ESPHome YAML driver for MCP2515 CAN Bus Transceiver Module? Submit a pull request or report corrections to our index.