Arduino+傾斜センサーを用いてグーグルストリートビューを動かす

## まえがき

#### 経緯

* 
ひょんなことから[arduinoスターターキット](https://www.amazon.co.jp/ELEGOO-Arduino%E7%94%A8UNO-R3-%E6%9C%80%E7%B5%82%E7%89%88%E3%82%B9%E3%82%BF%E3%83%BC%E3%82%BF%E3%82%AD%E3%83%83%E3%83%88-UNO%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB%E4%BB%98-Items/dp/B06Y56JV64)を頂いた。

 * **基礎的な電子部品**から**各種センサー**などが大量に付属した豪華なセットだった。
* 頂いたお礼も兼ねて、何かしら作ってみようと思った。
  * **最短の時間**で**見た目が派手なもの**を作りたかった
  * じゃあ前作ったやつを流用しよう！
 
というわけで前回作った[ジョイコンを用いたグーグルストリートビュー操作](https://qiita.com/NNNiNiNNN/items/f9fe8c592c9c347deeb6)の検知部分だけ作り変えます。

ストリートビューを動かすための基本的な構成は前回の記事↓を参照してください。
[リングフィットアドベンチャーの道具でグーグルストリートビューを歩き回る【Unity+GoogleMapsJavaScriptAPI】
](https://qiita.com/NNNiNiNNN/items/f9fe8c592c9c347deeb6)

## Arduino IDEの導入と基礎
以下はほとんど自分用のメモなので、飛ばしていただいて問題ないです。

Arduinoをまともに使うのが初めてだったので基礎部分から確認します。


### 導入
以下の記事↓などを参考に、Arduino IDEの導入とポート等の設定を行いました。
[Arduinoで開発を始める準備](https://qiita.com/hsgucci/items/d88c559b2c64e6570e90)

### 基礎的な関数

#### ```setup()```

Unityでいう```Start()```のイメージです。起動時に実行をします。

#### ```loop()```

Unityでいう```Update()```のイメージです。setup()の後に繰り返し実行します。

#### ```pinMode()```
引数としてintのピン番号と、```INPUT```か```OUTPUT```のどちらかの値の二つを取ります。これを用いてsetup()内から指定したピンが入力用か出力用か決定します。

特に、以下のような設定を行うとArduino内臓のLEDの設定になります。

```cpp

void setup() {
  // LED_BUILTIN=13に設定されている
  pinMode(LED_BUILTIN, OUTPUT);
}
```

これを光らせるには以下の関数を使います。

#### ```digitalWrite()```

引数としてintのピン番号と、```HIGH```か```LOW```のどちらかの値の二つを取ります。これを用いて指定したピンに対してHIGH, LOWの出力を行います。

先ほどのLEDはこれで光るようになります。

```cpp
digitalWrite(LED_BUILTIN, HIGH);
```

#### ```digitalRead()```

引数としてintのピン番号を指定します。指定されたピンへの入力を読み取ります。

#### ```Serial.print()```

シリアルポートへの出力を行います。```Serial.println()```だと改行付きです。

## 実際の設定
### 傾きの検知に用いるセンサー
Tilt Ball switchを用います。

![Honeyview_20210102_012400-1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356208/377defba-78d0-9514-7c3f-ed6e3c647062.jpeg)

構造としてはシンプルで、中に入っている金属球が物理的に移動することで接続が繋がったり切れたりするものです。

### 構成
画像はElegooによるマニュアルからの引用です。
![rapture_20210102013446.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356208/e069092f-c08f-0038-ff62-657c59c6e003.jpeg)

2番のピンとGNDに対してセンサーを繋げるだけです。

### Arduino側のコード

ピン2への入力の有無によってシリアルポートに1か0を出力します。基本的には付属していたサンプルを改変しただけのものです。

```cpp

void setup()
{ 
  pinMode(LED_BUILTIN,OUTPUT);
  pinMode(2,INPUT);
  digitalWrite(2, HIGH);
  Serial.begin(115200);
} 

void loop() 
{  
  int digitalVal = digitalRead(2);
  if(HIGH == digitalVal)
  {
    digitalWrite(LED_BUILTIN,LOW);
    Serial.println(0);
  }
  else
  {
    digitalWrite(LED_BUILTIN,HIGH);
    Serial.println(1);    
  }
}
```
## Unityの設定

基本的には[前回の記事](https://qiita.com/NNNiNiNNN/items/f9fe8c592c9c347deeb6)の構成をそのまま使います。

* 表示用ページを作成してAssets下に置く
* selenium+chromedriverの導入

が必要です。前回の記事を参照してください。






### シリアル通信への変更
検知手段が変わったので、前回ジョイコンを用いていた部分だけ書き換えます。

参考: [【Unity/Arduino】UnityとArduinoを連携してみた](https://rikoubou.hatenablog.com/entry/2018/01/16/175113)

上記の記事に従ってApi Compatibility Levelの設定を変更。また、シリアル通信用にコードを書き替えます。

serialPortへの接続と値の取得は参考サイトのコードをそのまま使います。message_として受け取った"0"か"1"の値が切り替わった瞬間にProceed()を動かし、表示したページをSeleniumで操作して前進します。

```cs
using UnityEngine;
using System.IO.Ports;
using System.Threading;


using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;


public class ArduinoWalker : MonoBehaviour
{
    private ChromeDriver _driver;
    int count = 0;
    public delegate void SerialDataReceivedEventHandler(string message);

    string portName = "COM7"; // ポート名(接続しているポート名を入れる)
    public int baudRate = 115200;  // ボーレート(Arduinoに記述したものに合わせる)

    private SerialPort serialPort_;
    private Thread thread_;
    private bool isRunning_ = false;

    private string message_;
    float time = 0f;
    float timeTheta = 0.3f;
    bool isOne = false;
    void Start()
    {
        var path = Application.streamingAssetsPath;
        _driver = new ChromeDriver(path);

        _driver.Navigate().GoToUrl(Application.dataPath + "/StreetView.html");

    }
    void Awake()
    {
        Open();
    }

    void Update()
    {
        time += Time.deltaTime;
        if (message_ == "1" && isOne == false)
        {
            Proceed();
            time = 0f;
            isOne = true;
        }
        else if (message_ == "0" && isOne == true)
        {
            Proceed();
            isOne = false;
        }
    }



    private void Open()
    {
        serialPort_ = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One);
        serialPort_.Open();

        isRunning_ = true;

        thread_ = new Thread(Read);
        thread_.Start();
    }

    private void Close()
    {
        isRunning_ = false;

        if (thread_ != null && thread_.IsAlive)
        {
            thread_.Join();
        }

        if (serialPort_ != null && serialPort_.IsOpen)
        {
            serialPort_.Close();
            serialPort_.Dispose();
        }
    }

    private void Read()
    {
        while (isRunning_ && serialPort_ != null && serialPort_.IsOpen)
        {
            try
            {
                message_ = serialPort_.ReadLine();
            }
            catch (System.Exception e)
            {
                Debug.LogWarning(e.Message);
            }
        }
    }

    void Proceed()
    {
        if (time > timeTheta)
        {
            count++;
            Debug.Log(count);
            _driver.FindElement(By.Id("count")).Click();
            time = 0f;
        }


    }

    void OnDestroy()
    {
        Close();
        _driver.Dispose();
    }
}
```
適当なgameobjectを配置して、このコードをつければ完成です。

## 動かしている様子

今回の場所は北アルプスです。

Arduino（に養生テープで固定したセンサー）を傾けるとシリアルポートへの出力を行い、UnityがUSB接続で受け取り、Seleniumを用いて対象のページを操作。前進していきます。

![Videotogif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356208/51f515aa-6232-0063-9eec-b198c82fc7fd.gif)

あとはこれをステッパーに取り付ければ、実際に歩きながらストリートビューの世界を進むことができます。
## 課題

現在の構成はあくまで有線での接続なので、実用性は微妙だと思います。無線でなんやかんやするための知識も欲しい。

あとはgoogle maps apiの機能を活かせてない(決めた座標から前進するだけ)ので、座標の決定や保存、ルート案内みたいな方向でも改善余地があります。

## あとがき

ほとんど前回の流用記事でした。マイコンでの電子工作(?)は経験がありませんでしたが、今回は構成を置き換えるだけで気軽に試せてよかったです。

[arduinoスターターキット](https://www.amazon.co.jp/ELEGOO-Arduino%E7%94%A8UNO-R3-%E6%9C%80%E7%B5%82%E7%89%88%E3%82%B9%E3%82%BF%E3%83%BC%E3%82%BF%E3%82%AD%E3%83%83%E3%83%88-UNO%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB%E4%BB%98-Items/dp/B06Y56JV64)の内容は本当に豪華かつ日本語マニュアルも充実していたので、もっといろいろ試してみたいですね。