ラズパイで部屋の見える化してみる、その2

# はじめに
前回から大分時間が空いてしまいましたが、社内新卒LTで発表したラズパイとLaravelを使ったセンサーデータの可視化の続きを書きます。
前回の[ラズパイで部屋の見える化してみる、その1](https://qiita.com/ringo156/items/43f12e1582381f081231)ではセンサーからデータの取得とcsvへの保存を行いました。
今回はLravelでAPI作ってデータベースにデータを保存までを書きます。
LaravelやMVCモデルについて詳しく解説というより、やったことを整理して残しておくためのメモ書きのつもりです。

# 手順
前回の記事を参照すると

1. センサーからデータの取得＆処理
2. 液晶に表示
3. csvに保存
4. クラウドにデータを投げる
5. DBに保存
6. webページ上で表示

となっているので今回は4~5を実装していきます。

# 環境構築
研修でLaravelというものを初めて触ったのでLaravelでバックエンドを作っていきます。
基本的に[VSCode の Remote - SSH機能を使ってConoha VPS上でLaravelの開発環境を用意する](https://qiita.com/Fendo181/items/3a6d77c2f4c7ac96c071) の説明どおりでLaravelの環境をセットアップし、マイグレーションまでできると思います。

作成したインスタンスのIPアドレスに接続するとLaravelのwelcomeページが表示されます。

![スクリーンショット 2020-08-04 10.14.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147601/7383b875-06b0-58fd-8114-87bd16eb9f86.png)


# データベースの設定

## マイグレーション
温湿度センサーのデータを格納するためのDBを用意します。直接DBを叩くのではなくLaravelのmigration機能を使ってテーブルを作成します。
まず、次のコマンドでマイグレーションファイルを作成します。

```
php artisan make:migration create_sensors_table --create=sensors
```

`database/migrations/`にマイグレーションファイルが生成されるので`public function up()`の中に以下のように追加します。

``` php
Schema::create('sensors', function (Blueprint $table) {
    $table->increments('id');
    $table->float('temperature');
    $table->float('pressure');
    $table->float('humidity');
    $table->integer('sensor_id');
    $table->timestamps();
});
```

`php artisan migrate`コマンドでマイグレーションが実行されます。
MySQLにログインして確認してみます。

```
MariaDB [(none)]> use laravel;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [laravel]> show tables;
+-------------------+
| Tables_in_laravel |
+-------------------+
| failed_jobs       |
| migrations        |
| sensors           |
| users             |
+-------------------+
4 rows in set (0.00 sec)

MariaDB [laravel]> desc sensors;
+-------------+------------------+------+-----+---------+----------------+
| Field       | Type             | Null | Key | Default | Extra          |
+-------------+------------------+------+-----+---------+----------------+
| id          | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| temperature | double(8,2)      | NO   |     | NULL    |                |
| pressure    | double(8,2)      | NO   |     | NULL    |                |
| humidity    | double(8,2)      | NO   |     | NULL    |                |
| sensor_id   | int(11)          | NO   |     | NULL    |                |
| created_at  | timestamp        | YES  |     | NULL    |                |
| updated_at  | timestamp        | YES  |     | NULL    |                |
+-------------+------------------+------+-----+---------+----------------+
7 rows in set (0.00 sec)
```

Sensorsテーブルとカラムが追加されていることが確認できます。

## モデルの作成

テーブルが作成されたのでLaravelからDBへアクセスできるようModelクラスを作成します。
以下のコマンドでモデルを作成します。

```
php artisan make:Sensor
```

appディレクトリに`Sensor.php`が作られるので、ファイルの中身をSensorsテーブルに合わせて次のように編集します。

``` php
<?php

namespace App;
use Illuminate\Database\Eloquent\Model;

class Sensor extends Model
{
    protected $fillable = [
        'temperature',
        'pressure',
        'humidity',
        'sensor_id',
    ];
}
```

これでDBと連携してデータを保存できるようになりました。
LaravelではModelクラスを呼び出すことでDBへデータを挿入したり取り出したりすることができます。
初めて触ったときはこの辺の動きがよく分からず、とりあえずコピペして動かしたら動いた状態でした。

# データを受け取るためのAPIを作る
今回はラズパイで収集したセンサーデータを受け取ってDBへ格納するだけなので、POSTで値を受け取る実装にします。

## コントローラの作成
実際にデータを受け取ってモデルを操作するコントローラを作成します。

``` 
php artisan make:controller SensorController
```
`app/Http/Controllers/SensorController.php`が作成されます。
このファイルにリクエストの処理を書きます。

``` php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Sensor;

class SensorController extends Controller
{
    public function store(Request $request)
    {
        $sensor = Sensor::create([
            'temperature' => $request->input('temperature') ,
            'pressure' => $request->input('pressure'),
            'humidity' => $request->input('humidity'),
            'sensor_id' => $request->input('sensor_id'),
            ]);
            return response()->json($sensor, 201);
    }
}
```
先程作ったModelクラスを`use App\Sensor;`で呼び出しています。
`Sensor::create`でDBへ指定した値を保存しています。
理解してしまえばシンプルで扱いやすいなと思いました。

## ルーティングを設定
APIのルーティングを設定します。
LaravelではAPIのルーティングは`routes/api.php`に書きます。

``` php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::middleware('api')->group(function () {
    Route::post('sensors', 'SensorController@store');
});
```
ルーティングではリクエストのメソッド、URI、呼び出すコントローラーを指定します。
ここではメソッドがpost、URIはsensors、コントローラーはSensorController@storeを指定しています。
実際にAPIを叩くときは`IPアドレス/api/sensors`にPOSTすることで`app/Http/Controllers/SensorController.php`のstoreメソッドが実行されます。

`php artisan route:list`コマンドでルーティングを確認することができます。

```
php artisan route:list
+--------+----------+-------------+------+---------------------------------------------+------------+
| Domain | Method   | URI         | Name | Action                                      | Middleware |
+--------+----------+-------------+------+---------------------------------------------+------------+
|        | GET|HEAD | /           |      | Closure                                     | web        |
|        | POST     | api/sensors |      | App\Http\Controllers\SensorController@store | api        |
+--------+----------+-------------+------+---------------------------------------------+------------+
```



# POSTしてみる

ここまででAPIはできたのでラズパイからPOSTできるか確認してみます。
確認のために簡単なプログラムを書きました。

``` python
import time
import random
import json
import urllib.request
url = `http://YourIPAddress/api/sensors`

def post_data(temp, press, hum):

    data = {
        'temperature' : temp,
        'pressure' : press,
        'humidity' : hum,
        'sensor_id' : 6,
    }
    headers = {
      'Content-Type': 'application/json',
    }
    req = urllib.request.Request(url, json.dumps(data).encode(), headers)
    try: urllib.request.urlopen(req)
    except urllib.error.URLError as e:
        print(e.reason)

while True:
    temp = random.uniform(10, 30)
    press = random.uniform(980, 1010)
    hum = random.uniform(50, 80)
    post_data(temp, press, hum)
    print("temp={0}, press={1}, hum={2}".format(temp, press, hum))
    time.sleep(1)
```
POSTリクエストはurllibを使って投げるようにします。
ここではPythonのrandom関数を使ってセンサーの値を擬似的に生成して1秒間隔で送信するようにしました。
MySQLにログインして実際にPOSTできたことを確認します。

![スクリーンショット 2020-08-05 12.37.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147601/bd75efb9-ea9b-4fb1-03b2-1b896860a3a0.png)

ここまで実装できると、前回の記事のセンサーデータの取得と組み合わせたらいい感じに表示できそうな気がしてきますね！

# 終わりに
今回はCONOHA上にインスタンスを立ててLaravelでAPIを作成からデータをPOSTしてDBへ保存するところまでを実装しました。
本当はデータの表示まで書きたかったのですが思ったより長くなったので次回に書こうと思います。
フロントエンド？知らない子ですね。。。

# 参考にした記事

- [VSCode の Remote - SSH機能を使ってConoha VPS上でLaravelの開発環境を用意する](https://qiita.com/Fendo181/items/3a6d77c2f4c7ac96c071)
- [はじめてのLaravelアプリケーションガイド](https://qiita.com/Fendo181/items/dece727ea402552fee19)
- [Laravel入門 - 使い方チュートリアル - ](https://qiita.com/sano1202/items/6021856b70e4f8d3dc3d)
- [Laravelのディレクトリ構造について学ぶ](https://qiita.com/shosho/items/93cbff79376c41c3a30b)
- [Python の HTTP クライアントは urllib.request で十分](https://qiita.com/hoto17296/items/8fcf55cc6cd823a18217)