That's Done!

thatsdone's (mostly technical) memorandum

[ja] Zephyr ESP32 notes


直近で書いた ZephyrをはじめましたZephyrの一般的な使い方メモ(ボード非依存) の記事の後、とりあえずESP32からいきます。

前提

私はEspressifのESP32-WROVER-E というボードを使っています。 CPUはTensilica XtensaアーキテクチャのLX6というだいぶ古いものです。 Zephyrのboardページだとここにあるものに対応します。

以下、シリーズによってはあてはまらない情報があるかもしれませんが、御承知おきを。

ESP32のCANコントローラ(TWAI)を使いたい

可能です。

ただし、少なくとも v4.2.0-4096-g1c1598601b5c 時点では、disabledにされています。 具体的に言うと以下のあたり。(2025/11/02時点)

https://github.com/zephyrproject-rtos/zephyr/blob/main/dts/xtensa/espressif/esp32/esp32_common.dtsi#L438

431                 twai: can@3ff6b000 {
432                         compatible = "espressif,esp32-twai";
433                         reg = <0x3ff6b000 DT_SIZE_K(4)>;
434                         interrupts = <TWAI_INTR_SOURCE IRQ_DEFAULT_PRIORITY 0>;
435                         interrupt-parent = <&intc>;
436                         clocks = <&clock ESP32_TWAI_MODULE>;
437                         status = "disabled"; # disabledになっている
438                 };

このため、overlayファイルを作ってオーバーライドしてあげる必要があります。 やり方は前の記事を参照してください。

ESP32でCAN通信をしたい

可能です。

ただし、ESP32はCANのコントローラしか内蔵していないので、CANトランシーバーを接続してあげる必要があります。

私は、MicrochipのCAN-FD対応のトランシーバーのMCP2562FDを使い、 この記事で紹介されている回路を組み、対向側としてRaspberry Pi Zero W + DSP SH-C31AおよびRenesas R-Car V4Hを搭載したSBCの Sparrow Hawk で動作確認しました。 MCP2562FDは秋月で200円(当時)でした。

2025/10/20に大阪で開催されたZephyr Project Meetup: Osaka, Japanで話した ネタ はこの話をしています。

会場で「普通はMicrochipのトランシーバを使うのか?」という質問を受けましたが、 CANは枯れた規格なので、基本的には何でも動くのではないかと思います。NXPのチップとか。動作電圧とかは注意ですが...

CANデバイスの状態を確認したい

微妙です。(...だと思います(4.2.0時点))

prj.confでCONFIG_CAN_SHELLを有効化(=y)すると使えるようになる shellのCANサポート機能を使い、

uart:~$ can show DEVICE_ID

等とやるとトランシーバーの状態まで表示できます。 使えるDEVICE_IDは、省略して can show とやると一覧表示されます。

以下は can show DEVICE_IDしてみた時のログの例になりますが、 外付けのCAN transceiverの場合は、ESP32とは関係なく、もうひと手間加えてあげないと、 Zephyr側で認識してくれないように思います。

$ west espressif monitor
(snip)
[00:00:00.175,000] <dbg> can_sja1000: can_sja1000_init: initial sample point error: 0
*** Booting Zephyr OS build v4.2.0-4096-g1c1598601b5c ***
(snip)

いきなり余談ですが...

  1. ESP32の場合は westの拡張が入っていて west espressif monitor とかできるので便利です
  2. 上記をみるとESP32が内蔵するCANコントローラはNXPのSJA1000互換のIPを使っているものと想像できます。

以下、状態表示してみた結果。

uart:~$ can show can@3ff6b000
core clock:      40000000 Hz
max bitrate:     1000000 bps
max std filters: 5
max ext filters: 5
capabilities:    normal loopback listen-only one-shot triple-sampling
mode:            normal
state:           error-active

ここのstate:はCANコントローラの状態です。error-というprefixが気になるのですが、コードを眺める限りは、これで正常に初期化できているということのようです。

rx errors:       0
tx errors:       0
timing:          sjw 1..4, prop_seg 0..0, phase_seg1 1..16, phase_seg2 1..8, prescaler 1..64
transceiver:     passive/none

次にここ。transceiver: が passive/none という状態になっていて、トランシーバの状態が取れない模様...うん?

statistics:
  bit errors:    0
    bit0 errors: 0
    bit1 errors: 0
  stuff errors:  0
  crc errors:    0
  form errors:   0
  ack errors:    0
  rx overruns:   0

しかし、このまま送信処理をしてみると、以下のように無事に送信処理が終わっている!?

uart:~$ can send can@3ff6b000  234 56
enqueuing CAN frame #0 with standard (11-bit) CAN ID 0x234, RTR 0, CAN FD 0, BRS 0, DLC 1
CAN frame #0 successfully sent
uart:~$

このログを採取した際、CANバスの対向側にRaspberry Pi Zero W+DSD SH-C31Aをつないでおり、実際にデータが流れていることを確認しています。

それで、上記transceiverの表示のところは、ソースの該当部分を眺めると、deviceに対応する管理構造体がない(=pointerがNULL)だとこういう表示になるようです。

と、いうことは...

  • dtsで何かtransceiver用の記述をしてあげないといけない?
  • 後述のCAN Busの状態をGPIOで制御したい場合と同様に、GPIO用のpin定義のついでに)deviceも書かないといけない?

...とか想像できますが、まだ裏がとれていません。

あとは、statistics: のところ、数字が入ってくれるのかどうか、まだ確認できていません。

うーん...

CAN Busの状態を制御したい(BUS Stopさせたり戻したりとか)

can_transceiver_enable() というAPIを使って可能(...なはず)です。 CANコントローラへの指示ではなく、トランシーバのSTBY(だったり、チップによってはSだったりしますが...)GPIOでピンの値を制御することになります。(はずです)

...とはいえ上記に書いたように、何かおまじないが必用...と思われます。

以下、参考。

  • CAN transceiverまわりの説明
    • https://docs.zephyrproject.org/latest/hardware/peripherals/can/transceiver.html
  • CAN transceiver APIの説明
    • https://docs.zephyrproject.org/latest/doxygen/html/group__can__transceiver.html

ESP32の複数コアを使いたい

可能です。

ESP32の場合は、普通のSMPではなく、 AMP(Asymmetric Multi-processing) と呼ばれる使い方になるようです。 ビルドの際に、先頭のコア用のプログラムはprocpu、 2番目(以降?)のコアがappcpuという名前でターゲット指定することになります。 sysbuildでブートローダにmcubootを指定し、2番目のコア(前述のappcpu)用のプログラムは remote というディレクトリにわけて作ることになるようですが、識者によると、ここでも「黒魔術」っぽさがある模様...gkbr

複数コアの間の通信や同期には IPM: Inter-Processor Mailbox という仕組みが使えるようになっています。 IPMを使ったプログラミングの具体例は、zephyr公式のサンプルにも含まれています。ESP32用のものはここにあります。

なお、threadを使うだけでは1コアしか使われないことに注意が必要です。(たぶん)

TODO

以下のような確認を行いながら更新してく予定。

  • multi-threadを使いたい
  • ESP32のWiFiを使いたい
  • ESP32でhttp_serverを動かしたい
  • gdbでリモート(?)デバッグしたい
  • ESP32にセンサ(I2CとかSPIとか)をつなぎたい
  • ...