Iot歯ブラシでlambdaる




10月にラスベガスで開催された「AWS re:Invent 2015」。AWSのCTO Werner Vogelsによる day2のキーノートでは、IoTについて様々な事例と共に、AWS IoTの発表がありました。その中で、私が特に気になったシーンはこちらです。

![day2_keynote.jpg](https://qiita-image-store.s3.amazonaws.com/0/63007/ecc352aa-31fb-aed7-c578-ed4ce6a5b3df.jpeg "day2_keynote.jpg")

既にIoT歯ブラシの時代の様です。Keynoteで取り上げられたこの電動歯ブラシについて調べたところ、どうやらP&G社から発売されている「世界初スマホとBluetooth連携する電動歯ブラシ」の様で、「ブラウン オーラルB」シリーズ上位モデルっぽい感じです。このスライドを見た瞬間から、私の心はIoT歯ブラシに鷲掴みされました。

## エンジニアは歯が命です
私の勤め先では、健康促進施策として、健康の維持・向上に必要な生活習慣の実践状況をポイント化して評価するという取り組みがあります。その１つとして、毎日の歯磨き情報の報告が必要なのですが、これがなかなか面倒なのです。良い取り組みなのですけどね。。。


そんな中で、掴んだIoT歯ブラシ情報。上手く使えば、歯磨きデータを自動登録することができそうな気がします。エンジニアとしては面倒な事はクラウドを使って自動化して、スッキリすべしということで、今回の１人プロジェクトがスタートしました。

## 実現したいこと
* 歯磨きの実施状況をタイムリーに会社に自動報告する仕組みをAWS上でつくる
* 「ブラン オーラルB」の上位モデルは家庭内稟議が通らないので、IoT歯ブラシを自作します。


## IoT歯ブラシを作る
まずはともあれ、データを取得するIoT歯ブラシを作成しなければなりません。ラズパイあたりで、センサーを自作する選択肢も考えましたが、今回はBlueTooth BLEに対応したスマホ紛失防止キーホルダーを使って手抜きします。スマホが無くなるとブルブル震えて振動してくれる賢い奴です。
![button.png](https://qiita-image-store.s3.amazonaws.com/0/63007/4389b191-fccf-a67a-10f3-758007cff8f1.png)


BLEとはBluetooth 4.0規格の一部で、各種のセンサーや体に身につける小型の装置などで利用されています。これを普通の歯ブラシにくっつけるともうそこには、IoT歯ブラシが！雰囲気的にはAWS Buttonと同じです。技適認証 + ダクトテープによる防水対応で、隙がありません。

![iottooth..png](https://qiita-image-store.s3.amazonaws.com/0/63007/6b04d5f6-9d5a-e993-92a3-83b1861dedae.png)



## IoT歯ブラシからのデータを受信する
歯磨きの際に、中央のボタンを押せば自動でデータを取得するようにします。そのデータの受け口にはiphoneを採用しましょう。世界2番目のスマホ連動歯ブラシかもしれません。

iphoneアプリでBLEを利用するサンプル実装はここらへんを参考にさせて頂きました。

* [iPhoneアプリでBluetooth通信を使うための基礎知識](http://www.atmarkit.co.jp/fsmart/articles/ios_sensor04/01.html)
* [『iOS×BLE Core Bluetoothプログラミング』という本を書きました](http://d.hatena.ne.jp/shu223/20150323/1427067357)

注意所としては、iphone5s ios9.1の場合、atmarkitさんのサンプルコードだとCBCentralManager の初期化タイミングが遅く、スキャンを開始するタイミングでまだ CBCentralManagerStatePoweredOn になってない場合があります。iphoneからデバイスに接続できない場合があるので、スキャンを開始するところを調整してあります。

![ap.png](https://qiita-image-store.s3.amazonaws.com/0/63007/cb93b6a3-679d-b0df-47d1-b523a320e8c3.png)


## AWS側の実装
歯ブラシデータを取得するのですから、もしユーザ数が増えた場合、特定の時間帯にスパイクが発生することが予想されます。その為、将来の拡張性を備えた設計にせねばなりません。

* AWS IoT
* Amazon Kinesis FireHose

のどちらかを使いたかったのですが、`ios sdk`の対応状況から今回は`Amazon Kinesis Stream+ lambda`を使って擬似FireHoseを試してみます。lambdaは折角なので、Python版を使ってみました。

![tooth.png](https://qiita-image-store.s3.amazonaws.com/0/63007/aa0c97c0-5c33-3aa4-5131-2b7ead0b07b8.png)

worker側にロジックを実装してあるので、連携部分を担当するlambdaファンクションは非常に
シンプルになります。

### kinesis to S3連携部分
こんな感じで簡単に実装できます。kinesisから取り出したレコードを`%Y-%m-%d-%H-%M-%S`というファイル名で指定されたbucketに保存します。また、IAMロール側で、S3,kinesisへのアクセス権を設定しておく必要があります。

```py
import base64
import json
import boto3
from datetime import datetime

print('Loading function')

s3 = boto3.client('s3')

def lambda_handler(event, context):
    
    
    print("Received event: " + json.dumps(event, indent=2))
    bucket = "<mybucket>"
    for record in event['Records']:
        # Kinesis data is base64 encoded so decode here
        payload = base64.b64decode(record['kinesis']['data'])
        print("Decoded payload: " + payload)
        
        key= datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
        try:
            response = s3.put_object(Bucket=bucket, Key=key,Body=payload.encode('utf-8'))
            return 0
        except Exception as e:
            print(e)
            print('Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.'.format(key, bucket))
            raise e
```

### S3 to SQS部分の実装
こちらも適当に。さくさくと書いておきます。

```py
import json
import boto3

print('Loading function')

s3 = boto3.client('s3')
sqs = boto3.resource('sqs')

def lambda_handler(event, context):
    #print("Received event: " + json.dumps(event, indent=2))

    bucket = event['Records'][0]['s3']['bucket']['name']
    key = event['Records'][0]['s3']['object']['key']
    try:
        s3_response = s3.get_object(Bucket=bucket, Key=key)
        message = s3_response['Body'].read() 
        queue = sqs.get_queue_by_name(QueueName='<myQueueName>')
        sqs_response = queue.send_message(MessageBody=message)
        
        return sqs_response.get('MessageId')

    except Exception as e:
        print(e)
        raise e
```

これで、スケーラブルな歯ブラシプラットフォームが完成しました。

## 今後の拡張
AWS IoTを利用する構成に変更し、歯科医師助手的(上司による管理)な機能を実装します。

1. 歯磨きデータを報告していないユーザに対しての通知機能
    * 歯ブラシを振動させる事で、歯を磨いていない事を家庭内に通知します。
1. 歯磨きデータの可視化機能

私　　　　：歯磨き報告なければ、歯ブラシがプルプル震えて警告してくれる機能どうですか？
助手的な方：気持ち悪いので入りません。

## 最後に
re:Invent楽しいよ！まだ参加したこと無い方は来年こそは是非参加を！