Arduinoの開発環境をNeoVimで整える【C++】

Arduinoの開発環境は、一般的にArduino IDEを用います。

このIDEはGUIソフトなのですが、実はArduino CLIというCLIツールも提供されています。

CLI好きにとっては、とても嬉しいツールですね。

問題点

しかし、Arduinoの開発(C++)をNeoVimでする場合、Arduinoライブラリの参照エラーなどが発生してしまいます。

このサイトや、このサイトの通りにやってみたのですが、うまくいかず苦戦しました。

そこで、私がうまくいったやり方を記録しようと思います。

前提

  • NeoVim導入済み
  • Homebrew導入済み
  • coc.nvim導入済み

C++の準備

まずは、C++のコンパイラ等を準備します。

# インストール
$ brew install llvm clangd

# インストール確認
$ clangd --version
Apple clangd version 13.1.6 (clang-1316.0.21.2.5)
Features: mac+xpc

ファイル保存時にオートフォーマットを適用したい場合は、以下を設定します。

(意図しないフォーマットになることがあるので、私は設定していないです)

$ nvim
:CocConfig

# 任意の場所に以下を挿入(カンマ区切りなどに注意する)
  "coc.preferences.formatOnSaveFiletypes": [
    "cpp"
  ]

Arduino CLIの準備

Arduino CLIをインストールします。

$ brew install arduino-cli

# インストール確認
$ arduino-cli version
arduino-cli  Version: 0.23.0 Commit: 899dc91b Date: 2022-06-06T14:23:44Z

コーディング

Arduino CLIで新規スケッチを作成します。

# スケッチの作成
$ arduino-cli sketch new SampleProject

# 中身の確認
$ tree SampleProject
SampleProject/
└── SampleProject.ino

# c++ファイルで実装したいので、inoファイルは空にしておく
$ cd SampleProject
$ cat /dev/null > SampleProject.ino

次に、C++でArduino本体のLEDを点滅させるプログラムを作成します。

vim main.cppで作成。

#include <Arduino.h>

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

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

現時点では、警告文が大量に出ると思います。

そこで、同一ディレクトリにcompile_flags.txtというファイルを作成し、必要なライブラリのパスを通してあげます。

-I/Users/ckpc/Library/Arduino15/packages/arduino/hardware/avr/1.8.5/cores/arduino
-I/Users/ckpc/Library/Arduino15/packages/arduino/hardware/avr/1.8.5/variants/standard
-I/Users/ckpc/Library/Arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/avr/include
-I/Users/ckpc/Library/Arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/include
-DUBRRH

上記は、私のPCでのパスですので、ご自身のPCでのパスを確認してから記載してください。

(特に、バージョン数などに注意)

しかし、この時点ではまだ警告文が発生すると思います。

後述のArduinoプラットフォームをインストールした後に、警告文が解消されます。

ちなみに、必要なライブラリを探す場合は(仮にServo.h)、ls -R $HOME/Library/Arduino15/packages/ | grep "Servo"などで検索しています。

ちょっと面倒ですね。何かいい方法あったら教えてください。。

コンパイル・デプロイ

ようやくここで、ArduinoをPCに接続します。

接続後、以下のコマンドで接続確認します。

$ arduino-cli board list
Error initializing instance: Error loading hardware platform: loading platform release esp8266:esp8266@2.7.4: loading boards: skipping loading of boards esp8266:esp8266:espduino: malformed custom board options
Error detecting boards: Error getting board list: [listing ports from discovery builtin:mdns-discovery: command failed: mdns lookup error: write udp6 [::]:59580->[ff02::fb]:5353: sendto: no route to host]
シリアルポート                         Protocol タイプ               Board Name  FQBN            Core
/dev/cu.BLTH                    serial   Serial Port       Unknown
/dev/cu.Bluetooth-Incoming-Port serial   Serial Port       Unknown
/dev/cu.usbmodem14401           serial   Serial Port (USB) Arduino Uno arduino:avr:uno arduino:avr

なんかエラーが発生していますが、とりあえずスルーしておきましょう。

重要なのは、シリアルポートの/dev/cu.usbmodem14401とFQBNのarduino:avr:unoとcoreのarduino:avrを記録することです。

この情報をもとに、必要なモジュールをインストールし、その後コンパイルします。

# モジュールインストール(前述のCoreの項目を指定する)
$ arduino-cli core install arduino:avr

# コンパイル(前述のFQBNの項目を指定する)
$ arduino-cli compile --fqbn arduino:avr:uno .
Error initializing instance: Error loading hardware platform: loading platform release esp8266:esp8266@2.7.4: loading boards: skipping loading of boards esp8266:esp8266:espduino: malformed custom board options
最大32256バイトのフラッシュメモリのうち、スケッチが924バイト(2%)を使っています。
最大2048バイトのRAMのうち、グローバル変数が9バイト(0%)を使っていて、ローカル変数で2039バイト使うことができます。


Used platform Version Path
arduino:avr   1.8.5

ここでも、なんかエラーが出でてますが、コンパイル自体は出来ているみたいなので、スルーします。

では、いよいよ、Arduinoにアップロードします。

# Arduinoにアップロードする(前述のシリアルポートとFQBNの項目を指定する)
$ arduino-cli upload -p /dev/cu.usbmodem14201 --fqbn arduino:avr:uno .

アップロードすると、Arduino本体のLEDがチカチカ点滅すると思います。

(また、main.cppファイルをNeoVimで開いてみると、警告文が解消されていると思います)

もうちょっと便利に

コンパイルやアップロードの度に、長ったらしいコマンドを叩いていては、少々疲れます。

そこで、シェルで少しばかり便利にしていきます。

まず、main.cppcompile_flags.txtのテンプレートを作っておきます。

# テンプレートディレクトの作成
$ mkdir 00_tmp

# flagsの作成
$ vim 00_tmp/compile_flags.txt

-I/Users/ckpc/Library/Arduino15/packages/arduino/hardware/avr/1.8.5/cores/arduino
-I/Users/ckpc/Library/Arduino15/packages/arduino/hardware/avr/1.8.5/variants/standard
-I/Users/ckpc/Library/Arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/avr/
-I/Users/ckpc/Library/Arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/incl
-DUBRRH

# main.cppの作成
$ vim 00_tmp/main.cpp

#include <Arduino.h>

void setup() {
}

void loop() {
}

次に、新しいスケッチを作成し、テンプレートで置き換えるシェルを作ります。

(引数で渡した名前で、スケッチを作成します)

$ vim new_sketch.sh

#!/bin/bash
arduino-cli sketch new $1
cp /dev/null $1/$1.ino
cp ./00_tmp/* ./$1

次に、コンパイル〜アップロードまでを行うシェルを作ります。

(引数で渡したスケッチ名をコンパイルし、アップロードまで行います)

$ vim compile_upload.sh

#!/bin/bash

# compile
arduino-cli compile --fqbn arduino:avr:uno $1

# upload
SERIAL=$(arduino-cli board list 2>/dev/null |grep usb |awk '{print $1}')
arduino-cli upload -p ${SERIAL} --fqbn arduino:avr:uno $1

これで、スケッチの作成からコンパイル・アップロードまでをシェルに任せることができます。

chmod +xで実行権限を与えるのを忘れずに)

例えば、こんな感じで使います。

# 最初の状態
$ tree .
.
├── 00_tmp
│   ├── compile_flags.txt
│   └── main.cpp
├── compile_upload.sh
└── new_sketch.sh

# 新しいスケッチを作成
$ ./new_sketch.sh 01_hoge
Sketch created in: /path/to/01_hoge

# 内容確認
$ tree .
.
├── 00_tmp
│   ├── compile_flags.txt
│   └── main.cpp
├── 01_hoge
│   ├── 01_hoge.ino
│   ├── compile_flags.txt
│   └── main.cpp
├── compile_upload.sh
└── new_sketch.sh

# main.cppを編集後、コンパイル・アップロードする
$ ./compile_upload.sh 01_hoge
Error initializing instance: Error loading hardware platform: loading platform release esp8266:esp8266@2.7.4: loading boards: skipping loading of boards esp8266:esp8266:espduino: malformed custom board options
最大32256バイトのフラッシュメモリのうち、スケッチが932バイト(2%)を使っています。
最大2048バイトのRAMのうち、グローバル変数が9バイト(0%)を使っていて、ローカル変数で2039バイ
  使うことができます。


Used platform Version Path
arduino:avr   1.8.5   /path/to/Library/Arduino15/packages/arduino/hardware/avr/1.8.5

Error initializing instance: Error loading hardware platform: loading platform release esp8266:esp8266@2.7.4: loading boards: skipping loading of boards esp8266:esp8266:espduino: malformed custom board options

こんな感じで、arduino-cliコマンドを毎回叩かなくてもスケッチ〜アップロードまでできるようになりました。

(コンパイルが一発で成功する前提のシェルになってます。気になる方は、コンパイル後の終了コードでもってアップロードを行うかどうかを分岐すると良いと思います)

最後に

やはり、開発ツールを整えることは、開発そのものを楽しくしてくれます。

Vimmerにとっては、Vimで開発ができるというだけで、かなりのモチベーションになりますよね。

よき開発ライフを。