Skip to content

CAN

RoboBoard X4 CAN pinout

CANH: CAN-High, CANL: CAN-Low, NC: Not Connected
V-: Ground, 5V: 5V output, V+: BATT rail output (~12V)

Ports are interconnected and used for daisy-chaining. Rated for 1Amp.
Cable is available in our store: 🛒 Totemmaker.net store → TOTEMBUS CABLE

Interact with CAN bus network connected over TBUS (TotemBUS) connector. Has dual purpose: Plug into CAN bus networks or attach X4 extension modules.

void setup() {
  // Start CAN peripheral at 500kbps
  CAN.begin(500);
}
void loop() {
  // Wait for CAN packet receive. 300ms timeout
  if (CAN.readPacketWait(300)) {
    // Get received packet
    auto packet = CAN.getPacket();
    // packet.id, packet.data, packet.len, packet.ext, packet.rtr
  }
  // Send CAN packets
  uint8_t data[8] = {1,2,3,4,5,6,7,8};
  CAN.writePacketExt(0x112233, data, 8); // Extended
  CAN.writePacketStd(0x1AB, data, 8);    // Standard
}

For more example code check: Examples → RoboBoard → CAN

Wiring

CAN bus wiring
X4 wiring with 3 MCP2515 modules

CAN bus network requires specific wiring with terminal resistors at each side of yellow and green wires. 120Ω resistor is already present inside X4. Additional one should be added to the last node in the network. May use resistors inside module (if available) or external one.

GUI Monitor

SavyCAN

RoboBoard X4 is compatible with open source SavvyCAN application to monitor CAN bus traffic. For setup instructions check totemmaker/ESP32RET repository.

Operating modes

CAN peripheral operates in 3 modes:

  • Normal - Send and received messages on CAN bus.
  • Listen - Receive messages without influencing the bus (monitoring).
  • Loopback - Sent messages will be received right away. Used for demo and testing purposes.

Supported baud (kBits): 25, 50, 100, 125, 250, 500, 800, 1000

CAN.begin(baud)

Start CAN bus peripheral in Normal mode.
Parameter: baud: CAN bus baud rate (kBits).

CAN.beginListen(baud)

Start CAN bus peripheral in Listen mode.
Note: There must be at least 2 devices on CAN bus for messaging to work, as RoboBoard won't send any packet acknowledgments. Can't use writePacket in this mode.
Parameter: baud: CAN bus baud rate (kBits).

CAN.beginLoopback(baud)

Start CAN bus peripheral in Loopback mode.
Works without attached to CAN bus network. Each sent packet will loop back to receiver.
Parameter: baud: CAN bus baud rate (kBits).

CAN.end()

Stop CAN peripheral. Required to change working mode or hardware filter.

state CAN.isRunning()

Check if CAN peripheral state is "running".
Returns: true: CAN is running. false: CAN is non-functional.

CAN.setEnable(state)

Switch CAN peripheral between "running" and "stopped" states.
Can be used to temporary stop packet transmit and reception.
Parameter:
state - true: put CAN to "running" state. false: put CAN to "stopped" state.

Writing

CAN.writePacketStd(id,data,len)

CAN.writePacketExt(id,data,len)

Send CAN data packet. "Std" and "Ext" differs in identifier size:
Standard: 11-bit identifier [0:0x7FF].
Extended: 29-bit identifier [0:0x1FFFFFFF].
Parameter:
id - identifier.
data - pointer to 8-bit array (or NULL if len = 0).
len - length of "data" array. [0:8].

CAN.writePacketStdRtr(id,len)

CAN.writePacketExtRtr(id,len)

Send Remote Transmission Request packet. "Std" and "Ext" differs in identifier size:
Standard: 11-bit identifier [0:0x7FF].
Extended: 29-bit identifier [0:0x1FFFFFFF].
Parameter:
id - identifier.
len - data length field. [0:8].

CAN.writePacket(ext,id,data,len,rtr)

Send CAN packet. Allows to select packet configuration trough parameters.
If rtr is set - parameter data is ignored.
Parameter:
ext - true: extended packet. false: standard packet.
id - identifier extended:[0:0x1FFFFFFF], standard: [0:0x7FF].
data - pointer to 8-bit array (or NULL if len = 0).
len - length of "data" array. [0:8].
rtr - true: send RTR packet. false: send data packet. Default: false.

Reading

Different message reception methods available. Only single one can be used at the time:

  • Event mode - messages will be received inside registered event handlers
    • Event is fired right after message is received
    • Multiple application modules can register event for CAN reception
    • Main loop is free to use for other application requirements
  • Polling mode - messages will be acquired using readPacket function inside a loop
    • Faster reception (bypassing event handling and context switching)
    • Better handles high amount of data

Event mode

CAN.addEvent(function)

Register CAN packet receive event. Can be read using getPacket().
Parameter:
function - function name.

CAN.removeEvent(function)

Unregister CAN packet receive event. Passed function won't be called anymore.
Parameter:
function - registered function name.

void eventCAN() {
  auto packet = CAN.getPacket();
  // packet.id, packet.data, packet.len, packet.ext, packet.rtr
}
void setup() {
  CAN.begin(500);
  CAN.addEvent(eventCAN);
}
void loop() {
  // Free to use
}

Polling mode

state CAN.readPacket()

Read received packet. Acquired packet is returned by function getPacket().
Each call will pull new packet from internal receive queue.
Returns: true: packet is received. false: no packets available.

state CAN.readPacketWait()

state CAN.readPacketWait(timeout)

Read received packet. Acquired packet is returned by function getPacket().
Each call will pull new packet from internal receive queue.
Function will halt code execution until new packet is received or timeout is passed.
Parameter:
timeout - (ms) time to wait for packet. Default: 0 - indefinitely.
Returns: true: packet is received. false: no packets available, timeout.

void setup() {
  CAN.begin(500);
}
void loop() {
  if (CAN.readPacketWait(300)) {
    auto packet = CAN.getPacket();
    // packet.id, packet.data, packet.len, packet.ext, packet.rtr
  }
}

CANPacket CAN.getPacket()

Return received CAN packet inside event function or after readPacket() returned true.
Returns: CANPacket structure, containing packet information.

struct CANPacket {
    uint16_t ext : 1; // Is extended packet (or standard)
    uint16_t rtr : 1; // Is Remote Transmit Request
    int16_t filter; // Software filter that accepted packet (otherwise -1)
    uint32_t id; // Packet identifier
    uint8_t len; // Packet data length
    uint8_t data[8]; // Packet data array
};

Filters

Filters will be applied to all received messages and intercepted only if id and mask parameters matches. Used to receive only specific packets, ignoring anything else.

Software

Software filters are running inside application, but has a few more advantages:
• Associate received packet with filter it matched packet.filter.
• Add and remove filters when CAN peripheral is running.
• Add up to 16 filters.

CAN.setFilterStd(num,id,mask)

Set standard software filter.
Parameter:
num - filter slot number [0:15].
id - identifier [0:0x7FF].
mask - validated identifier bits [0:0x7FF].

CAN.setFilterExt(num,id,mask)

Set extended software filter.
Parameter:
num - filter slot number [0:15].
id - identifier [0:0x1FFFFFFF].
mask - validated identifier bits [0:0x1FFFFFFF].

CAN.resetFilter(num)

Disable filter on selected slot (num).
Parameter:
num - filter slot number [0:15]. -1 - disable all filters.

Software filter example
void setup() {
    CAN.begin(500);
    // Allow to receive only standard packets:
    // 0x0A1 0x1A1 0x2A1 0x3A1 0x4A1 0x5A1 0x6A1 0x7A1
    // 0x0A2 0x1A2 0x2A2 0x3A2 0x4A2 0x5A2 0x6A2 0x7A2
    CAN.setFilterStd(0, 0xA1, 0xFF);
    CAN.setFilterStd(1, 0xA2, 0xFF);
}

Hardware

Hardware filters are limited, but efficient with high amount of data.
Notice:
• Must be configured before calling CAN.begin().
• Only single one of "setHardware" function can be used.

CAN.setHardwareFilterStd(id,mask)

Set single standard hardware filter.
Parameter:
id - identifier [0:0x7FF].
mask - validated identifier bits [0:0x7FF].

CAN.setHardwareFilterExt(id,mask)

Set single extended hardware filter.
Parameter:
id - identifier [0:0x1FFFFFFF].
mask - validated identifier bits [0:0x1FFFFFFF].

CAN.setHardwareFilterDual(id1,mask1,id2,mask2)

Set dual hardware filter. Can filter out 2 different standard packets.
Parameter:
id1, id2 - identifier [0:0x7FF].
mask1, mask2 - validated identifier bits [0:0x7FF].

CAN.setHardwareFilter(code,mask,single)

Set hardware filter manually. Allows for some additional configuration.
For meaning of code and mask read: ESP32 → TWAI → Acceptance Filter.
Parameter:
code - 32-bit code.
mask - 32-bit mask.
single - true: single filter, false: dual filter.

Hardware filter example
void setup() {
    // Allow to receive only standard packets:
    // 0x0A1 0x1A1 0x2A1 0x3A1 0x4A1 0x5A1 0x6A1 0x7A1
    CAN.setHardwareFilterStd(0xA1, 0xFF);
    CAN.begin(500);
}

Bus info

alerts CAN.readAlerts()

Read CAN peripheral alerts (if any). Returned value is reset each function call.
Returns:
alerts - 32-bit value of active alerts. See TWAI - Alerts for description.
Definitions are available in #include "driver/twai.h"

count CAN.readRxCount()

Read amount of queued RX messages.
Returns:
count - number of messages awaiting for read with readPacket.