陸上短距離走用 自動タイム測定器を作ろう(4)

前回の記事では、Visual Studio 2017 をインストールして、アプリのビルドと配置ができる環境を構築しました。
https://qiita.com/IKETA/items/ec4239e7eb337256425b

今回は、測距センサーを使用した自動タイム測定器の回路を組み立てて、チェック用の簡単なアプリを作成し動作確認をしてみます。


# 回路図
回路図は以下の通りです。回路の主役となる測距センサーは、安価な赤外線方式の GP2Y0A710K0F を使用します。1 ～ 5.5m 程度の距離をしか測定できませんが、今回はランナーのゴール判定でのみ使用するため、測定範囲や正確性は求めていませんのでこれで十分と考えます。

<img width="727" alt="TAFIoT-003.png" src="https://qiita-image-store.s3.amazonaws.com/0/153436/6e3bdb08-3c94-b6f0-ec2c-bd7dede35f97.png">


|番号|パーツ種|形名|参考|
|:--:|:--:|:--:|:--:|
|(1)|測距センサー|GP2Y0A710K0F|[データシート](http://www.sharp.co.jp/products/device/doc/opto/gp2y0a710k_e.pdf)|
|(2)|ADコンバーター|MCP3008|[データシート](http://ww1.microchip.com/downloads/en/DeviceDoc/21295C.pdf)|
|(3)|コンデンサー| |10μF 以上の容量|

この他に、ブレッドボードや配線用のジャンパー線が必要になります。いずれのパーツもアキバや通販などで簡単に購入できます。
実際に組み立てた感じは、以下のようになります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/153436/6ad2341e-ab0c-cc7c-4ae5-bdf6b24c8305.png)


## 各センサーの役割と詳細

### (1)測距センサー
測距センサーはゴールラインに向けて配置します。ランナーがセンサーの照射範囲を横切った瞬間に測定値が変化するので、これをゴールと判定します。

|コードカラー|シンボル|接続先|
|:--:|:--:|:--:|
|赤|GND|ラズパイの GND|
|白|Vo|ADコンバーターの CH0|
|黒|Vcc|ラズパイの 5V|
|青|Vcc|ラズパイの 5V|
|黄|GND|ラズパイの GND|

### (2)ADコンバーター
今回使用する測距センサーは、測定した距離を出力電圧の大きさで表現しますが、ラズパイで値を受け取れるようにデジタル化する必要があります。そこで、ADコンバーターを使用して、センサーの出力電圧であるアナログ信号をデジタル信号に変換します。

|ピン番号|シンボル|接続先|
|:-----:|:--:|:--:|:--:|
|1|CH0|測距センサーの Vo (出力)|
|2 ~ 8|CH1 ~ CH7|未使用|
|9|DGND|ラズパイの GND|
|10|CS/SHDN|ラズパイの GPIO8 (SPI_CE0)|
|11|Din|ラズパイの GPIO10 (SPI_MOSI)|
|12|Dout|ラズパイの GPIO9 (SPI_MISO)|
|13|CLK|ラズパイの GPIO11 (SPI_SCLK)|
|14|AGND|ラズパイの GND|
|15|Vref|ラズパイの 3.3V|
|16|Vdd|ラズパイの 3.3V|

### (3)コンデンサー
測距センサーの電源ライン安定化のために使用します。ただし、無くても構いません。


# 動作チェック用アプリ
これで電子回路の組み立てと、ラズパイとの接続が完了しました。ただし、これを動かすアプリがないと意味がないので、チェック用のアプリを作成して、測距センサーがどのような動きをするかを確認します。

## プログラムコード
[前回の記事](https://qiita.com/IKETA/items/ec4239e7eb337256425b) で作成した UWP アプリのプロジェクトを引き続き使用します。MainPage.xaml との MainPage.xaml.cs を以下のように書き直します。

```MainPage.xaml
<Page
    x:Class="App1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App1"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" Width="640" Height="360">
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock x:Name="textBlock1" Text="0" Margin="10,74,10,192" Height="94" Width="620" FontSize="72" TextAlignment="Center"/>
    </Grid>
</Page>
```

```MainPage.xaml.cs
using System;
using Windows.Devices.Enumeration;
using Windows.Devices.Spi;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace App1 {

    public sealed partial class MainPage : Page {

        private static readonly byte[] WriteBuffer = new byte[] { 1, 8 << 4, 0 };

        private SpiConnectionSettings _spiConSettings;
        private SpiDevice _spiDevice;
        private DispatcherTimer _timer = new DispatcherTimer();

        public MainPage() {
            this.InitializeComponent();
            this.InitializeSpiDevice();          

            _timer.Interval = TimeSpan.FromMilliseconds(1000);
            _timer.Tick += (sender, e) => {
                if (_spiDevice != null) {
                    byte[] readBuffer = new byte[3];
                    _spiDevice.TransferFullDuplex(MainPage.WriteBuffer, readBuffer);
                    this.textBlock1.Text = (((readBuffer[1] & 3) << 8) + readBuffer[2]).ToString();
                }
            };
            _timer.Start();
        }

        private async void InitializeSpiDevice() {
            var spiDevices = await DeviceInformation.FindAllAsync(SpiDevice.GetDeviceSelector("SPI0"));

            if (spiDevices.Count > 0) {
                _spiConSettings = new SpiConnectionSettings(0);
                _spiConSettings.ClockFrequency = 1000000;
                _spiConSettings.Mode = SpiMode.Mode0;

                _spiDevice = await SpiDevice.FromIdAsync(spiDevices[0].Id, _spiConSettings);
            }
        }
    }
}
```

SPI デバイスに接続するコードの解説は次回に繰り延べにさせていただいて、とりあえずこれをビルドして、ラズパイに配置します。

## 測距センサーの動作チェック
Windows IoT Remote Clinet で画面を確認しながらアプリを実行します。アプリが起動すると、以下のように数値が表示されて、1 秒ごとに数値が更新されます。数値は測定距離のデジタル値を 10 進数で表したもので、これだけでは距離がよくわかりませんが、とりあえず今は数値が変化することだけ確認できれば OK です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/153436/cc2fad64-f5ea-e2c5-9ae1-a5293957a6b6.png)

測距センサーの前で手をかざしたり、物を置いて近づけたり遠ざけたりすると、数値が変化することを確認します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/153436/5c79548e-6915-c638-cd58-bcfa2ba44a2a.png)

測距センサーを陸上のゴール判定として使用する場合は、装置をゴールに設置したときの「普段の測定距離」を記憶しておき、この数値が大きく変化した際に、ランナーがゴールを横切ったと判定することになります。

# まとめ
測距センサーを組み込んだ回路を作成し、これを動かすチェックアプリを使ってセンサーが動作することを確認しました。
次回は、ストップウォッチと組み合わせて、測定距離の変化に応じてストップウォッチが停止する仕組みを作ります。

# 参考文献
Pinout
https://pinout.xyz/

# 連載記事
陸上短距離走用 自動タイム測定器を作ろう
(1) https://qiita.com/IKETA/items/a32ada0053673264a6fb
(2) https://qiita.com/IKETA/items/7750ef533aa455340dae
(3) https://qiita.com/IKETA/items/ec4239e7eb337256425b