ESP-IDF 環境利用の基礎知識

はじめに

本ドキュメントでは C 言語で ESP32 マイコンのプログラミングをするやり方を述べる. 開発環境として ESP-IDF を用いる.

ESP-IDF 環境のリファレンス

ESP-IDF Programming Guide

初めの一歩: Hello World

プロジェクトの作成

ESP-IDF 環境でプログラミングを行う場合は, ESP-IDF 環境をインストールしたディレクトリ(ここでは $HOME/esp) 以下にプロジェクト用のディレクトリを作る.

$HOME/esp/esp-idf 以下に example ディレクトリがあるが, ここにサンプルが置かれている. 必要なサンプルを探してコピーして利用するのが簡単である.

プログラムの作成

最初の第 1 歩は Hello World である. サンプルがあるのでそれを流用する.

$ cd ~/esp

$ cp -r esp-idf/examples/get-started/hello_world .

$ cd hello_world

ディレクトリ構造は以下のようになっている. main/ の中に C 言語のプログラム (hello_world_main.c) が置かれている.

$ tree -L 3 
  .
  ├── CMakeLists.txt
  ├── Makefile
  ├── README.md
  ├── loadable_elf_example_test.py
  ├── main
  │   ├── CMakeLists.txt
  │   ├── component.mk
  │   └── hello_world_main.c
  └── sdkconfig.ci

試しに main/hello_world_main.c を見てみると良いだろう.

$ cat main/hello_world_main.c

  ...(略)...

ビルドする前に

ビルドする前に, ESP32 のデバイス名の確認を行う.

ESP32 は /dev/tty* というデバイス名で認識されていることが普通である. /dev/tty* を調べ, "USB" と付くもの or tty.<USB-Serial の型番> を探す. Linux の場合は /dev/ttyUSB0 となるのが普通である.

$ ls /dev/tty*

  ...(略)...

ビルド

make コマンドもしくは idf.py コマンドでビルドする. ESP-IDF の公式ドキュメントでは idf.py を使うことになっているが, make コマンドで全く問題ない.

ESP32 のデバイス名を確認した後に, make コマンドを実行する. プロジェクトのディレクトリで最初に make コマンドを実行した時には 以下の例のように config menu が表示される. TOP から Serial flasher config を選択し, "Default serial port" を先に調べたデバイス名 (以下の例では /dev/ttyUSB0) にする.

$ make

一番最初に make するときは 必要となるライブラリを全てコンパイルしているので時間がかかる. プロジェクトのディレクトリをみると, build という新たなディレクトリが 作成され, その下にオブジェクトファイルが置かれているのがわかる.

$ tree -L 3
  .
  ├── CMakeLists.txt
  ├── Makefile
  ├── README.md
  ├── build
  │   ├── app_trace
  │   │   ├── app_trace.d
  │   │   ├── app_trace.o
  │   │   ├── app_trace_util.d
  │   │   ├── app_trace_util.o
  │   │   ├── component_project_vars.mk
  │   │   ├── gcov
  │   │   ├── heap_trace_tohost.d
  │   │   ├── heap_trace_tohost.o
  │   │   ├── host_file_io.d
  │   │   ├── host_file_io.o
  │   │   └── libapp_trace.a
  │   ├── app_update
  │   │   ├── component_project_vars.mk
  │   │   ├── esp_app_desc.d
  │   │   ├── esp_app_desc.o
  │   │   ├── esp_ota_ops.d
  │   │   ├── esp_ota_ops.o
  │   │   ├── libapp_update.a
  │   │   └── tmp_cppflags.txt
  │   ├── asio
  │   │   ├── asio
  │   │   ├── component_project_vars.mk
  │   │   └── libasio.a

  ...(以下, 略)...

設定した内容はプロジェクトディレクトリ直下に置かれている sdkconfig ファイルに含まれている. デバイス名は CONFIG_ESPTOOLPY_PORT に値が設定されている. sdkconfig はテキストファイルであるため, 簡単に中身を確認することができる. CONFIG_ESPTOOLPY_PORT に設定されている値を確認したければ grep すれば良い.

$ grep CONFIG_ESPTOOLPY_PORT sdkconfig 

  sdkconfig:CONFIG_ESPTOOLPY_PORT="/dev/ttyUSB0"
補足: 最初の make の時には自動的に config menu が表示される. 2 回目以降で config menu を表示させたい場合は陽に make menuconfig コマンドを実行する必要がある.

マイコンへの書き込みと標準出力の表示

マイコンに書き込むのは make flash コマンド, 標準出力を表示するのは make monitor コマンドである. まとめて make flash monitor としても良い.

プログラムをマイコンに書き込む. 書き込みが終了すれば, "Leaving...." "Hard resetting via RTS pin..." といった表示がなされる.

$ make flash

  ...(中略)...

  App "hello-world" version: 1
  Flashing binaries to serial port /dev/ttyUSB0 (app at offset 0x10000)...
  esptool.py v2.9-dev
  Serial port /dev/ttyUSB0
  Connecting.......
  Chip is ESP32D0WDQ6 (revision 0)
  Features: WiFi, BT, Dual Core, Coding Scheme None
  Crystal is 40MHz
  MAC: 30:ae:a4:02:26:34
  Uploading stub...
  Running stub...
  Stub running...
  Configuring flash size...
  Auto-detected Flash size: 4MB
  Flash params set to 0x0220

  ...(中略)...

  Leaving...
  Hard resetting via RTS pin...

プログラム中で "printf("Hello world!\n");" と書いたのを表示させたければ, make monitor する必要がある. monitor を終了するのは Ctrl-] である.

$ make  monitor

  ...(中略)...

  Hello world!
  This is ESP32 chip with 2 CPU cores, WiFi/BT/BLE, silicon revision 0, 4MB external flash
  Restarting in 10 seconds...
  Restarting in 9 seconds...
  Restarting in 8 seconds...

  ...(以下略)...
monitor を終了するのは Ctrl-] である.

プログラムの修正と実行

デフォルトの hello_world_main.c は ESP32 チップの情報などを出しているので, include するヘッダファイルの数が多く, プログラムの行数も多い. 単に "hello world" を表示するだけであれば, 以下のようなよくある単純な hello world プログラムで十分である. 但し, 関数名は main でなく app_main にする必要がある. また試してみると分かるが, app_main の型は void でないといけない (int 型ではダメ).

$ cp main/hello_world_main.c main/hello_world_main.c.bk

$ vi main/hello_world_main.c  

エディタで以下のようなプログラムを書く.

1 #include <stdio.h>
2 
3 void app_main(void)
4 {
5   int i;
6   for (i = 0; i < 100; i++) {
7     printf("%02d, Hello world!\n", i);
8   }
9 }
  • 6-8 行目
    • for 文を使って "Hello World!" を 100 回表示.

書き終わったら, make して実行する.

$ make

$ make flash monitor

    ...(略)...
  0, Hello world!
  1, Hello world!
  2, Hello world!
  3, Hello world!
  4, Hello world!
    ...(中略)...
  98, Hello world!
  99, Hello world!

このプログラムは 100 回表示させたら "Hello World!" の表示が止まる. 教育ボードのリスタートスイッチ (緑色のボタン) を押すと, 再びプログラムが実行されることがわかるだろう.