Arduino（ESP32）でROSを動かす


# はじめに

Arduino (ESP-WROOM-32; ESP32) + ROSでchatterサンプルを動かすところまでを構築した。
その実績メモを残す。

ググれば類似記事はたくさん出てくるので差別化のため本記事の特徴を書いておく。

* ROSのバージョンがMelodic Morenia
* ROS環境をDocker上で構築した
  * ROSのArduino用シリアル通信ライブラリの抽出をビルドなしで行える

# 動作環境

* Windows 10 1809
    * Docker Desktop Community 2.0.0.3
    * Arduino IDE 1.8.9

手元に手頃なデスクトップPCがなかったのでOSはWindowsを使うことにし、Docker Desktopを使用してコンテナ内にROS環境を構築することにした。
Gazeboなどシミュレータ（GUI）を使う予定があるならこの方法は使えないことに注意。

ESP32とWindowsはUSBで接続し、Arduinoプログラムのコーディング・ビルド・書き込みはホストOS（Windows）上にインストールしたArduino IDEで行う。

ESP32と通信するROSはDockerコンテナ上に構築する。

上述のとおり、ESP32とWindowsは物理的に接続されているがこれは単に電源供給のためであり、ESP32とROSの間の通信はWiFiによる無線通信で行う。

# セットアップ

Docker Desktopのインストールは省略する。

## `rosserial`入りDockerイメージの作成

ROSのイメージファイルは`ros:melodic-robot`として配布されているが、
今回やろうとしているArduiono（ESP32）との通信には`rosserial`パッケージを別途インストールする必要がある。
`ros:melodic-robot`をrunしてからインストールしてもよいが、せっかくなのでDockerfileを作って`rosserial`のインストールまでイメージ化しておく。

1. 適当な空フォルダを用意する
    * 以降では`C:\tmp\ros_env`を用意したものとする
2. 本節下部に示すDockerfileを`C:\tmp\ros_env\Dockerfile`として置く
3. コマンドプロンプトやPowerShell上で`docker build -t local_ros/rosserial .\`を実行してイメージを作成する

Dockerfile

```dockerfile
FROM ros:melodic-robot

# install packages
RUN apt update && apt install -y \
        ros-melodic-rosserial \
        ros-melodic-rosserial-arduino \
    && \
    apt clean && \
    rm -rf /var/lib/apt/lists/*
```

## Arduino IDEのインストール

### (1)本体のインストール

[公式ページ](https://www.arduino.cc/en/Main/Software)からダウンロード・インストールする。

2019年5月現在なら「Windows Installer, for Windows XP and up」か「Windows app Requires Win 8.1 or 10」がいいと思う。

### (2)ESP32用プラグインのインストール

こちらの記事を参考にした。

[ESP32-WROOM-32](https://ht-deko.com/arduino/esp-wroom-32.html#03_02) 

上記記事の「**ESP32-WROOM-32**」-「**セットアップ**」-「**ソフトウェア**」-「**[Arduino core for the ESP3 のインストール (ライブラリマネージャ使用)]**」を参照。

### (3)`ros_lib`の抽出とインストール

ArduinoからROSを使うためには専用のC++ライブラリが必要になる（ライブラリとはいっても実体はヘッダファイルなのでビルドは不要）。
今回の構成ではROSをコンテナ上に構築したので、コンテナ上のファイルをホストにコピーする。

通常はgitリポジトリをcloneしビルド・インストールしてパスを通したところでライブラリを抽出するスクリプトを実行するのが正規の手順であるが、いちいちビルドするのは面倒なので自前でコピーする。

1. `cd C:\tmp`でカレントディレクトリを移動する
2. `mkdir docker`で作業用フォルダを作成する
3. `docker run -it --rm -v C:\tmp\docker:/root/host local_ros/rosserial`を実行してDockerコンテナを起動する
4. `cp -R /opt/ros/melodic/share/rosserial_arduino/src/ros_lib ~/host`を実行してライブラリをコピーする
5. `exit`を実行してコンテナを終了する
6. `C:\tmp\Docker\ros_lib`フォルダを`＜Arduinoのインストールフォルダ＞\libraries\ros_lib`として移動する

# ビルド

Arduino IDEを開き、以下のプログラムをESP32に書き込む。
プログラムにはWiFiルータのSSIDおよびパスワード、接続先となるIPアドレスが必要になる。
接続先となるIPアドレスについてはホストであるWindowsのIPアドレスでよい。後述のとおりコンテナの起動時にポートフォワーディングオプションをつけるためである。

```cpp
//
// このソースコードは以下の記事を参考にさせていただきました。
// https://gist.github.com/KobayashiRui/094ac01d9d3cd2445faa2a1ef103646f
//

#include <ros.h>
#include <std_msgs/String.h>
#include <WiFi.h>

const char SSID[] = "＜ここにWiFiルータのSSIDを書く＞";
const char PASSWORD[] = "＜ここにWiFiルータのパスワードを書く＞";
IPAddress server(＜ここにWindowsのIPアドレスを書く＞); // e.g.: IPAddress server(192, 168, 1, 3);
const uint16_t serverPort = 11411;

WiFiClient client;

class WiFiHardware {
  public:
  WiFiHardware() {};

  void init() {
    client.connect(server, serverPort);
  }

  int read() {
    return client.read();
  }

  void write(uint8_t* data, int length) {
    for(int i=0; i<length; i++)
      client.write(data[i]);
  }

  unsigned long time() {
     return millis();
  }
};


ros::NodeHandle_<WiFiHardware> nh;

std_msgs::String str_msg;
ros::Publisher chatter("chatter", &str_msg);

char hello[13] = "hello world!";

void setup()
{
  Serial.begin(115200);
  WiFi.begin(SSID,PASSWORD);
    Serial.print("WiFi connecting");

  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(100);
  }

  Serial.println(" connected");
  nh.initNode();
  nh.advertise(chatter);
  delay(10);
}

void loop()
{
  str_msg.data = hello;
  chatter.publish( &str_msg );
  nh.spinOnce();
  delay(1000);
}
```

# 実行

コンテナを起動してROSマスターを起動し、subscribeする。

前述のとおり接続先としてホストのIPアドレスを指定したため、コンテナの起動においてはポートフォワーディングオプションをつけていることに注意する。

1. `docker run -it --rm -p 11411:11411 -v C:\tmp\docker:/root/host local_ros/rosserial`を実行してコンテナを起動する
2. `roscore &`を実行してROSマスターを起動する
3. `rosrun rosserial_python serial_node.py tcp &`を実行してTCP/IPによるシリアル通信をsubscribeする
4. ESP32のUSBケーブルを差し直して電源を入れ直す
5. subscribeに成功したメッセージが表示されたのを確認する
6. `rostopic echo chatter`を実行して`"hello world!"`を受信していることを確認する

以上。