シェアサイクルのオープンデータ（GBFS）とM5Stackを使ってパーソナライズドサイネージを作ってみた！

##  背景

- 新たな移動の選択肢としてシェアサイクルが普及

- 通勤、通学、観光、ポタリングなど様々な用途で利用されている。

- GBFSというシェアモビリティの標準的なデータフォーマットがシェアサイクル事業者から公開された。

##  課題 

- ステーション（貸出・返却場所）に行ったが、自転車がないことがある

- 事前にスマホアプリで確認はできるが、「貸出台数」を画面に表示するまでに30秒から1分程度はかかってしまう。


## つくるもの
- ボタンを押すと、設定しておいたステーションの貸出可能自転車数が表示されるスイッチ

https://twitter.com/Keijipoon/status/1542078279708188673

## やることを3つのステップに分解
- シェアサイクルオープンデータより、貸出可能自転車台数を取得：AWS Lambda の設定
- M5Stackとデータをやり取りするAPIを用意する：AWS API Gateway の設定
- M5Stackで、API経由でデータを取得しディスプレイに表示：M5Stackの使い方

## シェアサイクルのオープンデータについて

以下２つの記事に詳しく書かれています。

GBFSについての概要はこちら

https://note.com/openstreet/n/n2f4b51cd52b3


GBFSのファイル、カラムについてはこちら

https://qiita.com/kumatira/items/f9229c21d9f0db3b5ae3



## ソフトウェアバージョン

- M5Stack Basic V2.6
- M5Burner 2.28
- UIFlow v1.9.8-en
- macOS Monterey

という組み合わせで突き進んでみたら、エラーたくさん出たけど、何とかプログラムを書き込める状態になりました。

## Step1 : AWS Lambda の設定

- 言語はPythonを選択
- Pythonのライブラリを使えるようにLayerにAmazon リソースネーム (ARN) の設定



:::note info
参考図書
[AWS Lambda実践ガイド 第2版](https://www.amazon.co.jp/AWS-Lambda%E5%AE%9F%E8%B7%B5%E3%82%AC%E3%82%A4%E3%83%89-%E7%AC%AC2%E7%89%88-impress-gear/dp/4295013307/ref=sr_1_1?crid=2SQJNXBDJHDN&keywords=aws+lambda%E5%AE%9F%E8%B7%B5%E3%82%AC%E3%82%A4%E3%83%89+%E7%AC%AC2%E7%89%88&qid=1665755575&qu=eyJxc2MiOiIxLjM0IiwicXNhIjoiMS4wNCIsInFzcCI6IjEuMTgifQ%3D%3D&sprefix=aws+lambda%2Caps%2C213&sr=8-1)
（第1章,第2章）
:::




LambdaでPythonのスクリプトを開発する際、外部ライブラリを利用するにはLayerを用意する必要があります。

幸いにも以下のリポジトリで、有志の方々が作成したLambdaのPythonライブラリLayerが公開されているので使わせていただきましょう。

https://github.com/keithrozario/Klayers

例として、東京リージョン（ap-northeast-1）でPython3.8を使う場合のパッケージLayer群は以下のサイトに一覧されています。

https://api.klayers.cloud//api/v2/p3.8/layers/latest/ap-northeast-1/html


サンプルコード
```python
import json, requests
import pandas as pd

def lambda_handler(event, context):
    
    #クエリパラメータを変数として利用する
    id = event['station-id']

    #URLへアクセスしてデータを取得
    url = requests.get("https://api-public.odpt.org/api/v4/gbfs/hellocycling/station_status.json")

    text = url.text

    #変換したいJSONファイルを読み込む
    df = pd.read_json(text)
    
    #ネスト（入れ子）構造になっている"data"という項目を展開する
    df_json_normal = pd.json_normalize(df['data'][0])

    df_json = df_json_normal.query('station_id == @id')[["station_id","num_bikes_available"]]
    print(df_json)

    output_text = str(df_json.iloc[0,1]) 

    return {
            'statusCode': 200,
            'id':event['station-id'],
            'body': output_text
            }
```





## Step2 : AWS API Gateway の設定

- REST API
- GETメソッド
- クエリパラメータの設定

:::note info
参考図書
[AWS Lambda実践ガイド 第2版](https://www.amazon.co.jp/AWS-Lambda%E5%AE%9F%E8%B7%B5%E3%82%AC%E3%82%A4%E3%83%89-%E7%AC%AC2%E7%89%88-impress-gear/dp/4295013307/ref=sr_1_1?crid=2SQJNXBDJHDN&keywords=aws+lambda%E5%AE%9F%E8%B7%B5%E3%82%AC%E3%82%A4%E3%83%89+%E7%AC%AC2%E7%89%88&qid=1665755575&qu=eyJxc2MiOiIxLjM0IiwicXNhIjoiMS4wNCIsInFzcCI6IjEuMTgifQ%3D%3D&sprefix=aws+lambda%2Caps%2C213&sr=8-1)（第6章）
[Web API: The Good Parts](https://www.amazon.co.jp/Web-API-Parts-%E6%B0%B4%E9%87%8E-%E8%B2%B4%E6%98%8E/dp/4873116864/ref=sr_1_1?keywords=web+api+the+good+parts&qid=1665755759&qu=eyJxc2MiOiIxLjI0IiwicXNhIjoiMC4zNiIsInFzcCI6IjAuNDcifQ%3D%3D&sprefix=webapi%2Caps%2C182&sr=8-1)
参考ブログ
[API Gateway + Lambda + Amazon Translateを使って翻訳API を作成してみる](https://www.aws-room.com/entry/api-translation)
[URLクエリパラメータ(クエリストリング)の意味とは。使い方は? 除外はすべき?](https://webtan.impress.co.jp/e/2012/04/26/12663#:~:text=%E3%80%8C%E3%82%AF%E3%82%A8%E3%83%AA%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%80%8D%E3%81%A8%E3%81%84%E3%81%86%E3%81%AE%E3%81%AF,%E6%AC%A1%E3%81%AE%E3%82%88%E3%81%86%E3%81%AB%E3%81%AA%E3%82%8B%E3%80%82&text=%E3%81%93%E3%81%AE%E5%A0%B4%E5%90%88%E3%80%81%E3%80%8C%3F,gaiq%E3%80%8D%E3%81%8C%E3%82%AF%E3%82%A8%E3%83%AA%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%81%A0%E3%80%82)
:::

クエリパラメーターを設定して、

```:API endpoint
https://example.com/groups/station-status?station-id=1234
```
のように、endpointの末尾でステーションIDを指定すれば、該当するステーションの自転車台数が取得できるようにしました。

例


| station-id | ステーション名 |  endpoint | 
|:----------:|:------------:|:------------:|
| 2468      | グランツリー 武蔵小杉店       |https://example.com/groups/station-status?station-id=2468 | 
| 5668     | ソフトバンク本社前（OpenStreet本社）     | https://example.com/groups/station-status?station-id=5668| 

また、ステーションIDについては、
あらかじめ以下のようなプログラムでステーション一覧を取得し、
シェアサイクルスイッチのボタンに反映させたいステーションのIDを調べておこう！

```python:station_info
import json
import requests
import pandas as pd

# URLへアクセスしてデータを取得
url = requests.get(
    "https://api-public.odpt.org/api/v4/gbfs/hellocycling/station_information.json")
text = url.text

# 変換したいJSONファイルを読み込む
df_json = pd.read_json(text)

# ネスト（入れ子）構造になっている"data"という項目を展開する
df_data = pd.json_normalize(df_json['data'][0])

# JSONをCSVへ変換して文字コードをutf8で出力
df_data.to_csv("hellocycling_station.csv", encoding='utf8', index=False)
```


## Step3 : M5Stackの設定



:::note info
参考図書
[アイデアをカタチにする! M5Stack入門&実践ガイド](https://www.amazon.co.jp/%E3%82%A2%E3%82%A4%E3%83%87%E3%82%A2%E3%82%92%E3%82%AB%E3%82%BF%E3%83%81%E3%81%AB%E3%81%99%E3%82%8B-M5Stack%E5%85%A5%E9%96%80-%E5%AE%9F%E8%B7%B5%E3%82%AC%E3%82%A4%E3%83%89-M5Stack-M5StickC%E5%AF%BE%E5%BF%9C/dp/4297126699/ref=sr_1_1?crid=YSBG27WNLABU&keywords=m5stack+%E6%9C%AC&qid=1665755159&qu=eyJxc2MiOiIzLjczIiwicXNhIjoiMy41MCIsInFzcCI6IjMuMzQifQ%3D%3D&sprefix=m5stack+%2Caps%2C224&sr=8-1)
の第8章「UIFlowでM5Stackをプログラミングする」を参考にしました。
:::

### M5Stack Basicを購入　

![1C99B4C2-AAB0-45A7-B063-85CC8ECC18B9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274029/10adb8c4-d603-1acb-f5e6-d51453d48973.png)

まずは以下のリンクからM5Stack Basicを購入

https://www.switch-science.com/products/7362

>2023年6月7日、M5Stack Basic v2.7が発売となりました。v2.6は販売終了となります。

**v2.6 は ¥6,500、ちなみに現在購入できる v2.7 は、¥6,391 （2024年1月現在）**
ちょっと安くなってうれしいですね。

https://www.switch-science.com/products/9010

### M5Stackの基本操作

USBケーブルなどで電源を供給していない状況で、
- 赤い電源ボタンを押すを起動
- 赤い電源ボタンをもう1度押すを再起動
- 2度連続で押すと電源OFF 


### M5Burner

UIFLOW FIRMWARE BURNING TOOL をダウンロード
![D7889418-41E0-42C8-BE57-CE276C6BD560.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274029/07202b82-bb9b-a381-2159-c3a176d15873.png)


https://docs.m5stack.com/en/download

M5Burner　をインストールして立ち上げると以下のような画面が現れる。

![A9D7EF74-3669-4028-BF8C-232A2DA92C38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274029/ea94dc50-1fd7-b21e-9508-e41d1089c7e9.png)

### M5Stack UIFlow の Wifi 設定方法
>毎回悩むので、備忘録を兼ねて綴ります。
ずっと、M5 Burnからやり直しかと勘違いしてましたが、
以下のように簡単にできるとは知らなかったです。

という文章から始まるのですが、まさに私も同じ状況でした。こんなに簡単にWifiの設定できたんだと感動しました。

https://tatsuya1970.com/?p=15508

### UIFlow ( https://flow.m5stack.com/ )　サーバーへアクセス

https://flow.m5stack.com/

![BF19947F-F537-4C87-9FD0-7314B5098852.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274029/1ba26e42-ea7b-b917-e2d8-134ae026604b.png)

やっていることとしては、**「インターネット経由で UIFlow と M5Stack を接続」** している。
その際に、M5Stackに表示された API KEY を入力して接続する。

「ボタンを押したら、APIでラック数のデータを取得しディスプレイに表示する」フローを作成する。



## パーソナライズドサイネージの完成

完成した様子をXに投稿したので御覧ください！
音も入ってます!

https://x.com/Keijipoon/status/1555105026280165376?s=20