esphome-docs/components/canbus/index.rst

370 lines
13 KiB
ReStructuredText
Raw Normal View History

2024-07-16 05:48:18 +02:00
CAN Bus
=======
.. seo::
:description: Instructions for setting up an CAN bus in ESPHome
:image: canbus.svg
:keywords: CAN
The Controller Area Network (CAN) bus is a serial bus protocol to connect individual systems and sensors
as an alternative to conventional multi-wire looms. It allows automotive components to communicate on a
single or dual-wire data bus at speeds up to 1Mbps.
CAN is an International Standardization Organization (ISO) defined serial communications bus originally
developed for the automotive industry to replace the complex wiring harness with a two-wire bus. The
specification calls for high immunity to electrical interference and the ability to self-diagnose and repair
data errors. These features have led to CANs popularity in a variety of industries including building
automation, medical, and manufacturing.
The current ESPHome implementation supports single frame data transfer. In this way you may send and
receive data frames up to 8 bytes.
With this you can transmit the press of a button or the feedback from a sensor on the bus.
All other devices on the bus will be able to get this data to switch on/off a light or display the
transmitted data.
The CAN bus itself has only two wires named Can High and Can Low or CanH and CanL. For the ESPHome
CAN bus to work, you need to select the device that has the physical CAN bus implemented.
You can configure multiple buses.
Any CAN bus node can transmit data at any time; any node can both send and/or receive any ``can_id`` value.
You must determine how to organize the ``can_id`` values; for example, you can set up a CAN bus network where
each node has a ``can_id`` it will use to broadcast data about itself. If a given node should (for example) turn
on a light, it can listen to the CAN bus for messages containing its specific ``can_id`` and react accodingly.
With this architecture, you can have multiple nodes able to control a light connected to a single, specific node.
Base CAN Bus Configuration
--------------------------
Each ``canbus`` platform extends the following configuration schema:
.. code-block:: yaml
# Example configuration entry
canbus:
- platform: ...
can_id: 4
on_frame:
- can_id: 500
use_extended_id: false
then:
- lambda: |-
std::string b(x.begin(), x.end());
ESP_LOGD("can id 500", "%s", &b[0] );
.. _config-canbus:
**Configuration variables:**
- **platform** (**Required**, :ref:`platform<platforms-canbus>`): One of the supported CAN bus :ref:`platforms-canbus`.
- **id** (*Optional*, :ref:`config-id`): Manually specify the ID used for code generation.
- **can_id** (**Required**, int): default *CAN ID* used for transmitting frames.
- **use_extended_id** (*Optional*, boolean): Identifies the type of ``can_id``:
- ``false``: Standard 11-bit IDs *(default)*
- ``true``: Extended 29-bit IDs
- **bit_rate** (*Optional*, enum): One of the supported bit rates. See :ref:`this table <esp32-can-bit-rate>` for a
list of supported bit rates by the internal CAN (TWAI) controllers of different ESP32 variants. Defaults to ``125KBPS``.
- ``1KBPS`` - Support by ``esp32_can`` depends on ESP32 variant
- ``5KBPS`` - Support by ``esp32_can`` depends on ESP32 variant
- ``10KBPS`` - Support by ``esp32_can`` depends on ESP32 variant
- ``12K5BPS`` - Support by ``esp32_can`` depends on ESP32 variant
- ``16KBPS`` - Support by ``esp32_can`` depends on ESP32 variant
- ``20KBPS`` - Support by ``esp32_can`` depends on ESP32 variant
- ``25KBPS``
- ``31K25BPS`` - Not supported by ``esp32_can``
- ``33KBPS`` - Not supported by ``esp32_can``
- ``40KBPS`` - Not supported by ``esp32_can``
- ``50KBPS``
- ``80KBPS`` - Not supported by ``esp32_can``
- ``83K3BPS`` - Not supported by ``esp32_can``
- ``95KBPS`` - Not supported by ``esp32_can``
- ``100KBPS``
- ``125KBPS`` - *Default*
- ``200KBPS`` - Not supported by ``esp32_can``
- ``250KBPS``
- ``500KBPS``
- ``1000KBPS``
- **on_frame** (*Optional*, :ref:`Automation <automation>`): An automation to perform when a
CAN frame is received. See :ref:`canbus-on-frame`.
.. _platforms-canbus:
Platforms
---------
.. toctree::
:maxdepth: 1
:glob:
*
Automations
-----------
.. _canbus-on-frame:
``on_frame`` Trigger
********************
This automation will be triggered when a CAN frame is received. The variables ``x`` (of type
``std::vector<uint8_t>``) containing the frame data, ``can_id`` (of type ``uint32_t``) containing the actual
received CAN ID and ``remote_transmission_request`` (of type ``bool``) containing the corresponding field
from the CAN frame are passed to the automation for use in lambdas.
.. note::
Messages this node sends to the same ID will not show up as received messages.
.. code-block:: yaml
canbus:
- platform: ...
on_frame:
- can_id: 43 # the received can_id
then:
- if:
condition:
lambda: 'return (x.size() > 0) ? x[0] == 0x11 : false;'
then:
light.toggle: light1
- can_id: 0b00000000000000000000001000000
can_id_mask: 0b11111000000000011111111000000
use_extended_id: true
remote_transmission_request: false
then:
- lambda: |-
auto pdo_id = can_id >> 14;
switch (pdo_id)
{
case 117:
ESP_LOGD("canbus", "exhaust_fan_duty");
break;
case 118:
ESP_LOGD("canbus", "supply_fan_duty");
break;
case 119:
ESP_LOGD("canbus", "supply_fan_flow");
break;
// to be continued...
}
**Configuration variables:**
- **can_id** (**Required**, int): The CAN ID which, when received, will trigger this automation.
- **can_id_mask** (*Optional*, int): The bit mask to apply to the received CAN ID before trying to match it
with *can_id*. Defaults to ``0x1fffffff`` (all bits of received CAN ID are compared with *can_id*).
- **use_extended_id** (*Optional*, boolean): Identifies the type of ``can_id`` to match on. Defaults to ``false``.
- **remote_transmission_request** (*Optional*, boolean): Whether to run for CAN frames with the "remote
transmission request" bit set or not set. Defaults to not checking (the automation will run for both cases).
``canbus.send`` Action
**********************
The CAN bus can transmit frames by means of the ``canbus.send`` action. There are several ways to use it:
.. code-block:: yaml
on_...:
- canbus.send:
data: [ 0x10, 0x20, 0x30 ]
canbus_id: my_mcp2515 # optional if you only have 1 canbus device
can_id: 23 # override the can_id configured in the can bus
on_...:
- canbus.send: [ 0x11, 0x22, 0x33 ]
- canbus.send: 'hello'
# Templated; return type must be std::vector<uint8_t>
- canbus.send: !lambda return {0x00, 0x20, 0x42};
**Configuration variables:**
- **data** (**Required**, binary data, :ref:`templatable <config-templatable>`): Data to transmit, up to eight
bytes/characters are supported by CAN bus per frame.
- **canbus_id** (*Optional*): Sets the CAN bus ID to use for transmitting the frame. Required if you are have multiple
CAN bus platforms defined in your configuration.
- **can_id** (*Optional*, int): Allows overriding the ``can_id`` configured for the CAN bus device.
- **use_extended_id** (*Optional*, boolean): Identifies the type of ``can_id``:
- ``false``: Standard 11-bit IDs *(default)*
- ``true``: Extended 29-bit IDs
- **remote_transmission_request** (*Optional*, boolean): Set to send CAN bus frame to request data from another node.
If a certain data length code needs to be sent, include the necessary (dummy) bytes in ``data``. Defaults to ``false``.
Extended ID
-----------
Standard IDs and Extended IDs can coexist on the same segment.
.. note::
It is important to know that "standard" and "extended" addresses denote different addresses. For example,
Standard ``0x123`` and Extended ``0x123`` are, in fact, different addresses.
Decimal or hexadecimal notation may be used for IDs:
- Standard IDs use ``0x000`` to ``0x7ff`` (hexadecimal) or ``0`` to ``2047`` (decimal)
- Extended IDs use ``0x00000000`` to ``0x1fffffff`` (hexadecimal) or ``0`` to ``536870911`` (decimal)
This example illustrates how different ID types may be used in your configuration for both transmitting and receiving.
.. code-block:: yaml
# Transmission of extended and standard ID 0x100 every second
time:
- platform: sntp
on_time:
- seconds: /1
then:
- canbus.send:
# Extended ID explicit
use_extended_id: true
can_id: 0x100
data: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]
- canbus.send:
# Standard ID by default
can_id: 0x100
data: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]
canbus:
- platform: ...
can_id: 0x1fff
use_extended_id: true
bit_rate: 125kbps
on_frame:
- can_id: 0x123
use_extended_id: true
then:
- lambda: |-
std::string b(x.begin(), x.end());
ESP_LOGD("CAN extended ID 0x123", "%s", &b[0]);
- can_id: 0x123
then:
- lambda: |-
std::string b(x.begin(), x.end());
ESP_LOGD("CAN standard ID 0x123", "%s", &b[0]);
Binary Sensor Example
---------------------
Given that we have a button connected to a remote CAN node which will send a message to ID ``0x100`` with the payload
``0x1`` for contact closed and ``0x0`` for contact open, this example will look for this message and update the state
of its ``binary_sensor`` accordingly.
.. code-block:: yaml
binary_sensor:
- platform: template
name: CAN Bus Button
id: can_bus_button
canbus:
- platform: ...
can_id: 4
bit_rate: 125kbps
on_frame:
- can_id: ${0x100}
then:
- lambda: |-
if(x.size() > 0) {
switch(x[0]) {
case 0x0: // button release
id(can_bus_button).publish_state(false);
break;
case 0x1: // button press
id(can_bus_button).publish_state(true);
break;
}
}
Cover Example
-------------
In this example, three nodes are connected to the CAN bus:
- Node 1 sends a one-byte payload to ID ``0x50B``
- Node 2 sends a one-byte payload to ID ``0x50C``
These nodes send the following one-byte payload which is based on the state of a button connected to each of them:
- 0: Button release
- 1: Button press
- 2: Long press
- 3: Long release
- 4: Double-click
- Node 3 controls a motor connected to it. It expects a message to ID ``0x51A`` where the one-byte payload is:
- 0: Off
- 1: Open
- 2: Close
.. code-block:: yaml
canbus:
- platform: ...
id: my_canbus
can_id: 4
bit_rate: 125kbps
on_frame:
- can_id: 0x50c
then:
- lambda: |-
if(x.size() > 0) {
auto call = id(TestCover).make_call();
switch(x[0]) {
case 0x2: call.set_command_open(); call.perform(); break; // long press
case 0x1: // button press
case 0x3: call.set_command_stop(); call.perform(); break; // long release
case 0x4: call.set_position(1.0); call.perform(); break; // double-click
}
}
- can_id: 0x50b
then:
- lambda: |-
if(x.size() > 0) {
auto call = id(TestCover).make_call();
switch(x[0]) {
case 0x2: call.set_command_close(); call.perform(); break; // long press
case 0x1: // button press
case 0x3: call.set_command_stop(); call.perform(); break; // long release
case 0x4: call.set_position(0.0); call.perform(); break; // double-click
}
}
cover:
- platform: time_based
name: Canbus Test Cover
id: TestCover
device_class: shutter
has_built_in_endstop: true
open_action:
- canbus.send:
data: [ 0x01 ]
canbus_id: my_canbus
can_id: 0x51A
open_duration: 2min
close_action:
- canbus.send:
data: [ 0x02 ]
canbus_id: my_canbus
can_id: 0x51A
close_duration: 2min
stop_action:
- canbus.send:
data: [ 0x00 ]
canbus_id: my_canbus
can_id: 0x51A
See Also
--------
- :apiref:`canbus/canbus.h`
- :ghedit:`Edit`