CAN bus/Using it/07. The first message
Using it · 07 of 9

The first message

Two ESP32-S3 boards, two TK109s and three wires between the terminals. One sketch goes on both, with one line that says which board sends: the sender puts a counter on the bus once a second, and the receiver prints it. Both at 500 kbit/s, the rate the boards were tested at.

What you need

  • Two ESP32-S3 dev boards, each on its own USB cable.
  • Two TK109 boards, with their 120 Ω left on: two boards are the two ends.
  • Jumper wires, and three wires between the two bus connections.

Wire it

Each ESP32-S3 to its own TK109:

TK109ESP32-S3
GNDGND
3V33V3
CTXGPIO 8
CRXGPIO 9

Then the bus, board to board: CANH to CANH, GND to GND, CANL to CANL, on the three pins or the screw terminals. CTX and CRX go straight across; nothing is crossed.

Both ends at one rate

Both ends at one bit rate
rates differ
The receiver's sketch sets
Sender
500 kbit/s
Receiver
250 kbit/s
Bits read
4 for 8 sent
250 against 500 kbit/s: the receiver reads 4 bits for 8. Nothing it receives is a valid frame, so it answers with error frames, which break up the sender's message too, and both error counters climb. Put the same timing line in every sketch on the bus.

CAN has no clock wire. Every controller times the bits itself from the rate its sketch set, so every sketch on a bus has to use the same timing line. This one uses TWAI_TIMING_CONFIG_500KBITS().

What it prints

On the receiver:

receiver
got 0x100: 0
got 0x100: 1
got 0x100: 2

On the sender:

sender
sent 0, tx errors 0
sent 1, tx errors 0

tx errors 0 is the line that proves the bus: every frame was acknowledged. If you upload the sender first, its count climbs while it waits for the receiver, then falls back one per good frame once the receiver starts.

The code

tk109_first_message.ino

Set SENDER to true on one board and false on the other. The sender queues a one-byte frame with identifier 0x100 every second and prints its transmit error counter; the receiver prints every frame it gets.

// TK109 CAN: one sketch for both ESP32-S3 boards.
// Set SENDER true on one board and false on the other.
//
// Wiring, each ESP32-S3 to its own TK109:
//   TK109 GND -> ESP32 GND     TK109 3V3 -> ESP32 3V3
//   TK109 CTX -> GPIO8         TK109 CRX -> GPIO9
// Bus: CANH to CANH, GND to GND, CANL to CANL.
// Keep both 120R fitted: two boards are the two ends.
//
// Arduino IDE: Tools > Board > esp32 > ESP32S3 Dev Module,
// Tools > USB CDC On Boot > Enabled, then Tools > Port.
// Serial Monitor at 115200. No libraries to install.

#include "driver/twai.h"

const bool SENDER = true;
const int CAN_TX = 8, CAN_RX = 9;
const uint32_t ID = 0x100;

void setup() {
  Serial.begin(115200);
  delay(500);
  twai_general_config_t g = TWAI_GENERAL_CONFIG_DEFAULT(
      (gpio_num_t)CAN_TX, (gpio_num_t)CAN_RX, TWAI_MODE_NORMAL);
  twai_timing_config_t t = TWAI_TIMING_CONFIG_500KBITS();
  twai_filter_config_t f = TWAI_FILTER_CONFIG_ACCEPT_ALL();
  if (twai_driver_install(&g, &t, &f) != ESP_OK ||
      twai_start() != ESP_OK) {
    Serial.println("CAN did not start: check the pins");
    while (true) delay(1000);
  }
  Serial.println(SENDER ? "sender" : "receiver");
}

void loop() {
  if (SENDER) {
    static uint8_t count = 0;
    twai_message_t msg = {};
    msg.identifier = ID;
    msg.data_length_code = 1;
    msg.data[0] = count;
    twai_transmit(&msg, pdMS_TO_TICKS(100));
    delay(1000);
    twai_status_info_t s;
    twai_get_status_info(&s);
    Serial.printf("sent %u, tx errors %lu\n", count++,
                  (unsigned long)s.tx_error_counter);
  } else {
    twai_message_t msg;
    if (twai_receive(&msg, pdMS_TO_TICKS(1000)) == ESP_OK) {
      Serial.printf("got 0x%03lX: %u\n",
                    (unsigned long)msg.identifier, msg.data[0]);
    }
  }
}

driver/twai.h is Espressif's own CAN driver and comes with the ESP32 core, so there is nothing to install. On a classic ESP32 or an ESP32-C3, change CAN_TX and CAN_RX to two free GPIOs, such as 5 and 4.

When it does not work

The sender prints tx errors 8, 16, 24 and on up to 128.

Nobody is acknowledging. The receiver is off, still being uploaded, or its CAN controller did not start: open its Serial Monitor and look for the word receiver. Then check its CRX goes to GPIO 9 and the three bus wires join CANH to CANH, CANL to CANL, GND to GND.

It prints CAN did not start.

twai_driver_install() refused the pins or the driver was already installed. Check CAN_TX and CAN_RX are GPIOs your board has and nothing else in the sketch uses. On a classic ESP32, GPIO 8 and 9 belong to the flash chip; use 5 and 4.

The Serial Monitor on the ESP32-S3 shows nothing at all.

Tools > USB CDC On Boot must be Enabled for Serial to reach the USB port on most ESP32-S3 boards. Set it, upload again, and reopen the Serial Monitor.

The receiver prints nothing, and the sender's errors stay at 0.

The sender's frames are being acknowledged by something, so the bus works, but the receiver's sketch is not reading: check SENDER is false on that board and that both sketches use the same TWAI_TIMING_CONFIG line.

Where this goes next

How far 500 kbit/s reaches, and what 1 Mbit/s costs on this board.

Speed and length

Edit this page — content/books/can-bus/first-message.mdx

Community

Questions about this product

See what other owners have asked, and read their solutions.

Ask a question ↗

CAN Bus

Loading discussions…

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse Modules and blocks on the forum