PlatformIOの紹介

これは東工大ロボット技術研究会のアドベントカレンダー [rogy Advent Calendar 2022](https://adventar.org/calendars/7915) 10日目の記事です。

---

突然ですが、マイコン(Arduino, ESP32など)のプログラミング環境って独自IDE(Arduino IDEとか)に依存しているものが多いですよね。で、そういうIDEはVSCodeに比べるとやはり↓のような面が物足りないがちです。
- 補完機能
- テキスト編集 (正規表現を用いた検索、置換)
- シンタックスハイライト
- フォント、テーマなど各種設定

また、Arduino IDEなどでは外部のライブラリ([USB_Host_Shield 2.0](https://github.com/felis/USB_Host_Shield_2.0)など)はプロジェクトごとではなくシステム全体で同じものが共有される場合が多く、GitHubなどで他人とプロジェクトを共有するとこの点が問題になることもあります。

PlatformIOはこういった不満を解消することができます。実際に開発してみて特に問題なく快適に進められたので、開発を進めて溜まった知見をここで共有しようと思います。

## PlatformIO is 何

[What is PlatformIO? — PlatformIO latest documentation](https://docs.platformio.org/en/latest/what-is-platformio.html)などを参考にまとめたものが↓になります。

- モダンなIDE([VSCode](https://code.visualstudio.com/)とか)で開発できる
- どのOSでも動く
- [デバッグ](https://zenn.dev/karaage0703/books/80b6999d429abc8051bb/viewer/898591)、[ユニットテスト](https://qiita.com/takutoy/items/c684f761c655d832e5d2)、[CI/CD](https://www.redhat.com/ja/topics/devops/what-is-ci-cd)とかが楽にできる
- CLI(コマンドライン)でもIDEの提供するGUI上でも使える

IDEの提供するGUIですが、例えばVSCodeでは[PlatformIOの拡張機能](https://marketplace.visualstudio.com/items?itemName=platformio.platformio-ide)が提供されており、これをインストールすることでVSCodeがPlatformIOの開発環境になります。

(個人的にこの拡張機能は好きじゃないので)ここではこの拡張機能を入れずに、PlatformIOをCLIで使用する方法を紹介します。

## 開発環境

Raspberry Pi 4B(以下ラズパイ)に(MacBook Airから)SSH接続して、ラズパイ上でPlatformIOを使います。

ラズパイで使用するOS/microSDは次の通りです。

- OS: Ubuntu 22.04.1 LTS (64-bit)
    - [Raspberry Pi Imager](https://www.raspberrypi.com/software/)から焼く
- microSD: 16GB

:::note
ラズパイの環境構築では↓の記事を参考にしました。

- [Raspberry Pi OS をヘッドレスインストールする](https://zenn.dev/aruneko/articles/bab667eb3d39cd)
- [Raspberry Pi Documentation - Getting Started #Advanced Options](https://www.raspberrypi.com/documentation/computers/getting-started.html#advanced-options)
:::

## Installation

シェルで進めていきます。まずはPlatformIOのインストール

```bash
$ sudo apt install python3 python3-pip
$ python3 -m pip install -U pip
# ↓のようなエラーメッセージが出た場合は一度シェルを開き直す(SSH接続であればexitしてから再びログインする)
# WARNING: The scripts pip, pip3 and pip3.10 are installed in '/home/pi/.local/bin' which is not on PATH.
$ pip3 install -U platformio
$ pio --version
# エラーが出ないことを確認
```

次に、ラズパイからPlatformIOでマイコンと通信できるようにします。

```bash
$ curl -fsSL https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/system/99-platformio-udev.rules | sudo tee /etc/udev/rules.d/99-platformio-udev.rules
$ sudo service udev restart
$ sudo usermod -a -G dialout $USER
$ sudo usermod -a -G plugdev $USER
```

Macの場合は[homebrewのパッケージ](https://docs.platformio.org/en/latest/core/installation/methods/brew.html)をインストールしてください。

Windowsでやりたい方は[Installer Script (Recommended) — PlatformIO latest documentation](https://docs.platformio.org/en/latest/core/installation/methods/installer-script.html)を参考にしましょう。

- [System Requirements — PlatformIO latest documentation](https://docs.platformio.org/en/latest/core/installation/requirements.html)
- [Python Package Manager — PlatformIO latest documentation](https://docs.platformio.org/en/latest/core/installation/methods/pypi.html)
- [99-platformio-udev.rules — PlatformIO latest documentation](https://docs.platformio.org/en/latest/core/installation/udev-rules.html)

## Demonstration

ここではLチカのプロジェクト`blink`を作成してPlatformIOの機能を見ていきます。

参考: [Quick Start — PlatformIO latest documentation](https://docs.platformio.org/en/latest/core/quickstart.html)

### ボード指定

まずはプロジェクトで使用するボードをPlatformIOに教えてあげる必要があります。そのためにはPlatformIOで設定されているボードのIDを知る必要があるのですが、このIDを知る方法として公式サイトでは次の3つが紹介されています。

1. [Boardのカタログページ](https://docs.platformio.org/en/latest/boards/index.html)を見る
2. [Boardの検索ページ](https://registry.platformio.org/search?t=platform)で検索する
3. [`pio boards`コマンド](https://docs.platformio.org/en/latest/core/userguide/cmd_boards.html)を使う

ですが、「platformio <ボード名>」のようにググると検索上位にボードの情報が出てきます。例えば、私が「platformio arduino mega」とググった場合は↓のページがトップに出てきました。

- [Arduino Mega or Mega 2560 ATmega2560 (Mega 2560) — PlatformIO v6.1 documentation](https://docs.platformio.org/en/stable/boards/atmelavr/megaatmega2560.html)

このページの途中に、次のような記述があります。

> ## Configuration
>
> Please use `megaatmega2560` ID for [board](https://docs.platformio.org/en/stable/projectconf/section_env_platform.html#projectconf-env-board) option in ["platformio.ini"(Project Configuration File)](https://docs.platformio.org/en/stable/projectconf/index.html#projectconf):
>
> ```ini
> [env:megaatmega2560]
> platform = atmelavr
> board = megaatmega2560
> ```
>

(Arduino Megaを使う場合は)この`megaatmega2560`が、PlatformIOに教えるボードのIDになります。

ググってもPlatformIOのサイトが出てこなかった場合はブログ記事を覗くのもいいでしょう。参考(およびメモ)として、私がよく使うボードのIDを貼っておきます。

- [Arduino Uno](https://docs.platformio.org/en/latest/boards/atmelavr/uno.html): `uno`
- [ESP32-Devkit (Espressif ESP32 Dev Module)](https://docs.platformio.org/en/latest/boards/espressif32/esp32dev.html): `esp32dev`

### プロジェクト作成、確認

ボードのIDがわかったら、次はプロジェクト作成です。

```bash
# 適当な場所にcdしてから実行
$ mkdir blink
$ cd blink
$ pio init -b esp32dev
```

`pio init -b esp32dev`の`esp32dev`を使用するボードのIDに置き換えましょう。PlatformIOが、指定したIDのボードに対応するツール等がインストールされているか自動で確認してくれます。初めて指定するIDの場合はここで少し時間がかかるので気長に待ちましょう。

それでは、プロジェクト作成時の状態を確認しておきましょう。現時点でのプロジェクト構造は次のようになっているはずです。

```bash
$ tree -a
.
├── .gitignore
├── include
│   └── README
├── lib
│   └── README
├── platformio.ini
├── src
└── test
    └── README

4 directories, 5 files
```

各`README`にはそれぞれのフォルダの役割が記されています。各フォルダには次のような役割があります。

フォルダ    | 役割
:-------- | :-----
`include` | ライブラリとして使用する際に外部に公開されるヘッダーファイルが入る
`lib`     | プロジェクト内でのみ使用するヘッダーファイルが入る
`src`     | ソースファイルは全てここに入る
`test`    | ユニットテストで使用するプログラムが入る

`platformio.ini`はプロジェクトの設定ファイルです。こちらも確認しておきましょう。

```ini:platformio.ini
; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
```

`.gitignore`はgit管理で使用するものです。ここでは省略します。

### ぷろぐらみんぐ

`src`フォルダ内に`main.cpp`を作り、その中にお馴染みのLチカプログラムを書いていきます。

:::note
プログラムの編集にVSCodeを使う場合は`blink`ディレクトリ直下で`pio init --ide vscode`を実行してからVSCodeで[C/C++ Extension Pack](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools-extension-pack)を入れるといい感じになります。

ラズパイなどでの開発ですが、VSCodeでは[Remote - SSH](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh)を使ってSSH接続先で開発できます。詳細は↓の記事を参照してください。

- [Visual Studio Code で Remote SSH する。 - Qiita](https://qiita.com/nlog2n2/items/1d1358f6913249f3e186)
- [Developing on Remote Machines using SSH and Visual Studio Code](https://code.visualstudio.com/docs/remote/ssh)
:::

```cpp:main.cpp
#include <Arduino.h>

void setup() {
    pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(1000);
    digitalWrite(LED_BUILTIN, LOW);
    delay(1000);
}
```

### いざビルド

コンパイルをして、ボードに書き込むかどうかでコマンドが変わります。

- コンパイルだけする場合: `pio run`
- コンパイルしてボードに書き込む場合: `pio run -t upload`

プロジェクト作成時と同様、書き込みの際にも必要なツール群の確認/インストールが自動で行われます。

---

ここまでが基本的なPlatformIOの使い方です。

## シリアルモニター

PlatformIOはシリアルモニターを表示するコマンドも提供しています。具体的な方法を確認しましょう。まずは`main.cpp`に次のプログラムを書きます。(お好みで新しいプロジェクトを作成してください)

```cpp:main.cpp
#include <Arduino.h>

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

void loop() {
    Serial.println("Hello, world!");
    delay(1000);
}
```

これをボードに書き込んでシリアルモニターを開くと、1秒ごとに`"Hello, world!"`が出力されるはずです。それでは実際にやってみましょう。

```bash
$ pio run -t upload
$ pio device monitor -b 115200
--- Terminal on /dev/ttyUSB0 | 115200 8-N-1
--- Available filters and text transformations: colorize, debug, default, direct, esp32_exception_decoder, hexlify, log2file, nocontrol, printable, send_on_enter, time
--- More details at https://bit.ly/pio-monitor-filters
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
...
```

出力が確認できたかと思います。シリアルモニターは`ctrl + C`で停止できます。

シリアルモニターを開くのに`pio device monitor -b 115200`というコマンドを実行しました。`-b 115200`はボーレートの設定です。省略すると`9600`になります。

## git管理

コンパイル時に作られるバイナリファイルなどは全て`.pio`フォルダ内に置かれます。このフォルダはプロジェクト作成時にできる`.gitignore`に書かれているため、誤ってバイナリファイルをコミットすることは起こりにくいです。安心して`git add .`ができます。

ここでは深掘りしませんが、プロジェクトが依存しているライブラリの情報は全て`platformio.ini`に記述します。そして、PlatformIOはビルド時などに自動的に`platformio.ini`から必要なライブラリを割り出し、`.pio`フォルダ内にインストールしてくれます。このように、プロジェクト単位でライブラリのインストールが行われ、その情報も容易にgit管理対象に含まれるため、GitHubなどでプロジェクトを共有したときに発生する問題はほとんどないです。

## いかがでしたか

このように、PlatformIOではIDEに依存しない開発が行えます。ここでは紹介しませんでしたが、PlatformIOは以下の機能も備えています。

- ヘッダーファイルを使用したファイル分割
- GitHubなどで公開されているライブラリの使用
- 複数ボードに対するバイナリ生成
- ユニットテスト
- デバッガー
- CI/CD

詳細は[PlatformIOのドキュメント](https://docs.platformio.org/en/latest/)を参照してください。

## Appendix: 実際に開発で使った例

実は[夏休み中にロ技研内でSSR杯という新入生ロボコンが行われた](https://twitter.com/titech_ssr/status/1571022509184811008?s=20&t=5jy1IPMd484OE1e0h8uzIA)のですが、私が参加したチームではPlatformIOを用いてプログラムの開発を行いました。実際のプロジェクトは[GitHubで公開](https://github.com/ssr2022-saka-maza/comp2)しています。(チームのプログラム担当は私一人だったのでissueやPR、ソースコード内のコメントすらありませんが)

大会本番に使ったのはこのプロジェクトですが、準備期間中の途中までは[別のプロジェクト](https://github.com/ssr2022-saka-maza/competition)を使用していました。こちらは[大会用に自作したライブラリ](https://github.com/ssr2022-saka-maza/ssr)を使用していたのですが、途中で設計に無理があるとわかったので新しくプロジェクトを作成しました。

ライブラリの使い方などで参考になれば幸いです。