2026年9月6日日曜日

ESP32の内蔵回路でCAN通信


背景

ESP32内蔵の回路とCANのトランシーバーを組み合わせてCAN通信する方法を把握しました。
備忘録を兼ねて記事に残します。

使ったもの

  • ESP32DevKitC
    今回利用するマイコンESP32が載った開発ボードです。 
  • MCP2562FD
    CANのトランシーバーです。
  • ブレッドボード
    部品の信号線を引き出します。
  • ジャンパワイヤ
    ブレッドボード上は硬いもの、ブレッドボード間は柔らかいものを利用しました。
  • 120Ω抵抗
    CAN busの終端抵抗に利用します。
  • microB-USBケーブル
    ESP32へのプログラム書き込みに利用します。
  • CAN2.0の通信相手
    以前取り組んだCAN2.0の通信を可能にしたteensy4.1との通信を行います。
    通信速度は500kbpsで行います。
  • プログラム書き込み環境 PlatformIO
    ubuntu24.04 にVSCodeとPlatformIOをインストールした環境を使いました

ESP32の内蔵回路はCAN2.0

ESP32のデータシートにTWAI Controllerという名前で説明があり、それによるとCAN2.0に対応しており、信号線はGPIOのどれにでも割り当てられるとのことでした。

ESP32でCANを扱う回路

利用するライブラリESP32_CANの標準割当RXが16、TXが17にCANのトランシーバーを繋ぎます。


繋ぐとこうなりました。


プログラム

ESP32_CANライブラリを利用するために、platformioのlib_depsに関連ライブラリを記述します。

lib_deps =
https://github.com/collin80/esp32_can
https://github.com/collin80/can_common

記事を書いている時点で利用したライブラリや環境はこちらです。
PlatformIO Core, version 6.1.19
PACKAGES: 
 - contrib-piohome @ 3.4.4 
 - framework-arduinoespressif32 @ 3.3.2 
 - framework-arduinoespressif32-libs @ 5.5.0+sha.07e9bf4970 
 - tool-esptoolpy @ 5.1.0 
 - toolchain-xtensa-esp-elf @ 14.2.0+20250730
Dependency Graph
|-- ESP32_CAN @ 0.3.1+sha.c329e6b
|-- can_common @ 0.4.0+sha.8585f9d

ESP32_CANで内蔵CAN回路をコールバックなしで扱うプログラムです。
ESP32_CANのサンプルプログラムを参考にしました。
#include <Arduino.h>

#include <esp32_can.h>

void setup() {
Serial.begin(115200);

Serial.println("Initializing ...");

if (CAN0.begin(500000)) {
Serial.println("Builtin CAN Init OK ...");
} else {
Serial.println("Builtin CAN Init Failed ...");
}

CAN0.watchFor(); // allow anything through

Serial.println("Ready ...!");
CAN_FRAME txFrame;
txFrame.rtr = 0;
txFrame.id = 0x123;
txFrame.extended = false;
txFrame.length = 4;
txFrame.data.byte[0] = 0x10;
txFrame.data.byte[1] = 0x1A;
txFrame.data.byte[2] = 0xFF;
txFrame.data.byte[3] = 0x5D;
CAN0.sendFrame(txFrame);
}

byte i = 0;

// CAN message frame
CAN_FRAME message;

unsigned long sentAt = 0;

void loop() {
if (CAN0.read(message)) {
Serial.println("received at " + String(millis()));
Serial.print("ID: ");
Serial.println(message.id, HEX);
Serial.print("Extended: ");
if (message.extended) {
Serial.println("Yes");
} else {
Serial.println("No");
}
Serial.print("Length: ");
Serial.println(message.length, DEC);
for (i = 0; i < message.length; ++i) {
Serial.print(message.data.byte[i], HEX);
Serial.print(" ");
}
Serial.println();
}

if (millis() - sentAt >= 2000UL) {
sentAt = millis();
Serial.println("send data at " + String(sentAt));
message.id++;
for (i = 0; i < message.length; ++i) {
message.data.byte[i];
}
CAN0.sendFrame(message);
}
}

500kbpsでCANを有効にしています。
  if (CAN0.begin(500000)) {
Serial.println("Builtin CAN Init OK ...");

ESP32_CANの標準割当はRXが16、TXが17ですが、利用するピンを変えたい場合はsetCANPins関数で指定します。
  CAN0.setCANPins(GPIO_NUM_16, GPIO_NUM_17);  // rx, tx
if (CAN0.begin(500000)) {

2秒ごとにidが0xAA、送信データの最初がマイコン内部時計の秒数の情報を送信します。
  if (millis() - sentAt >= 2000UL) {
sentAt = millis();
Serial.println("send data at " + String(sentAt));
message.id = 0xAA;
for (i = 1; i < message.length; ++i) {
message.data.byte[i];
}
message.data.byte[0] = sentAt / 1000;
CAN0.sendFrame(message);
}

動作確認

CAN2.0 500kbpsで通信するTeensy4.1と繋いで情報をやりとりします。



Teensy側で情報の最初が期待通り2ずつ増えているのを確認できました。
teensy sent at 292846
MB 99 OVERRUN: 0 LEN: 8 EXT: 0 TS: 36194 ID: AA Buffer: EA 2 3 4 5 6 7 8 at 293229
teensy sent at 294847
MB 99 OVERRUN: 0 LEN: 8 EXT: 0 TS: 53082 ID: AA Buffer: EC 2 3 4 5 6 7 8 at 295229

ESP32側でTeensyから情報を受け取れているのを確認できました。
send data at 236000
received at 237619
ID: E0
Extended: No
Length: 8
1 2 3 4 5 6 7 8

おわり

ESP32とCANのトランシーバーを組み合わせ、ESP32に内蔵されたCAN 2.0の回路でCAN通信出来ました。
気になっていた機能の使い方が分かってよかったです。

参考

ESP32_CAN
ESP32のデータシート

0 件のコメント :