IoT時代のプログラミング（主にMQTTについて）

# IoTにおけるソフトと特徴

- Arduino
  - イベント駆動
- MQTT
  - 軽量プロトコル
  - publish/subscribe型のMQ

---

# [Arduino](https://www.arduino.cc/)とは

- 無償で使えるマイコン用プログラム作成アプリ
  - マイコン毎にアドオンがある(ex, [ESP8266](https://github.com/esp8266/Arduino))
- スケッチ(=プログラム)はsetup/loopで構成
  - setup: 電源入り後一度だけ呼ばれる
  - loop: (電源切るまで)繰り返し呼ばれる
- 提供されているスケッチ例を改造して動かす

```
$brew cask install arduino
```

---

# MQTTとは

- Message Queuing Telemetry Transport
- 非常に軽量なプロトコル
  - 2000年代初めに登場し[標準化](http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html)
  - TCP/IP上で動く
- publish/subscribe型のメッセージ転送
- IoT/モバイルに適する
  - センサーデバイス
  - Facebook Messengerで使われている

---

## HTTPよりも

- 軽量（ヘッダが最小2バイト）[^1]・優れた処理速度・省電力
  - HTTPではヘッダのオーバーヘッドが高い
  - AgostoのテストによるとHTTPSの10倍以上オーバーヘッドが低い
- IoTの利用場面に適している
  - 貧弱な電波環境（通信帯域制限）
  - バイト毎に料金・バッテリーを消費する環境

---

### HTTPヘッダとの比較

```
$curl --head https://www.yahoo.co.jp/
HTTP/1.1 200 OK
Date: Tue, 18 Jul 2017 18:10:57 GMT
(中略)
$curl --head https://www.yahoo.co.jp/ | wc -c
703
```

- 最小まで減らすとしたら`HTTP/1.1 200 OK`の15byte
- MQTTでは最小2バイト[^1]
- 通信量が膨大になるほどこの10byteの差が意味を持つ

---

### 暗号化

- TLSを使った暗号接続が可能
  - port: 8883
- ただし通信毎に認証が必要なので軽量さは失われる[^2]
  - メッセージ全体のサイズが大きいならヘッダの10バイト差は誤差
  - MQTTとHTTPsどちらが適しているか要検討

---

### HTTPとのすみ分け

protocol | 適した用途
--- | ---
http | 1対1で画像など大きなデータをやりとりしたい, secureな通信がしたい
mqtt | 複数台から小さいデータを大量にもらいたい

---

## Publish/Subscribe

![pub-sub.jpg](https://qiita-image-store.s3.amazonaws.com/0/100881/f2964e2c-775a-bf4e-35e8-53f5afd6c80b.jpeg)

- メッセージの送信者(publisher)が特定の受信者(subscriber)に直接メッセージを送信しない
- メッセージのやりとりにはBrokerと呼ばれる中継serverが必要
  - サーバーがメッセージを保管するため受信側の状態に関係なくメッセージを送れる
      - オフラインでもOK
  - publisher/subscriberがMQTT client
- 接続時のデバイスIDが被ると古い接続が切れる

出典: [Securing MQTT - BuildingIoT 2016 slides](https://www.slideshare.net/dobermai/securing-mqtt-buildingiot-2016-slides)

---

## message(= topic + data)

![pub-sub-model.png](https://qiita-image-store.s3.amazonaws.com/0/100881/e5d47fe6-711b-57ff-1256-e3087b318432.png)

1. subscriberはbrokerへtopicの受信を申し込む
  - 接続後一度だけで良い
1. brokerはtopicへのpublishがあると**接続中の**topicを申し込んだsubscriberへmessageを送信
  - 過去分は送れない（最後のmessageはオプションで受け取れる）
  - publisherにsubscriberのアクセス情報は通知されない

出典: [Paho Python client for MQTT and G-code Visualization Talks, Chennaipy](http://www.shakthimaan.com/posts/2015/10/13/chennai-py-talks/news.html)

---

### topic

- 一般的に/(スラッシュ)で階層化[^3]
  - ex, tokyoA/temp
- ワイルドカード(#,+)
- mosquittoなどの一部ブローカーにはACL機能有り

---

### QoS

- client間のメッセージ到達保証レベル(Quality of Service)
- 通常は0、コントロール系のメッセージは1以上、が基本
  - 1以上はbrokerでstoreが必要
- Kafkaの例だが[佐伯さんのスライド](https://www.slideshare.net/laclefyoshi/apache-kafka-011-exactly-once-semantics)がわかりやすい

level | desc | example
--- | --- | ---
0 | At most once(メッセージ到達保証無) | subscriberの受信失敗時のケアがない、など
1 | At least once(届くけど重複するかも) | subscriberのACK受信失敗時に再送、など
2 | Exactly once(確実に重複無く届ける) | transaction制御、速度低下

---

#### （閑話）ログの質が精度を分ける

- 米テックジャイアントのログ収集は実は雑
  - 重複やロストは気にしない
  - ログ設計はすごい。デモグラなど混ぜて1レコードに豊富な情報量
- 重複もロストも許さないログでディープラーニングにかけたら、モデルの精度がテックジャイアントより高かった
  - precision/recallがほぼ1
  - 1レコードは購買やPVなど基本情報のみ
  - 日本向けサービス同士の比較なので、日本語の壁など外的要因な可能性も
 
※社外秘のため詳細は公開しません

---

# MQTTブローカー

- OSS
  - [mosquitto](https://github.com/eclipse/mosquitto)
- cloud
  - [cloudMQTT](https://www.cloudmqtt.com/)
  - [ニフティクラウド](http://cloud.nifty.com/service/mqtt.htm)
- 公開MQTTテスト用ブローカー
  - [iot.eclipse.org](https://eclipse.org/paho/clients/js/utility/)

---

# MQTTクライアント

- OSS
  - [paho](https://eclipse.org/paho/)
      - 様々な言語に対応
  - [PubSubClient](https://github.com/knolleary/pubsubclient)
      - Arduinoクライアント
      - 日本語のスケッチ例は[こちら](https://bitbucket.org/monoxit/iot101/src/4784af02195fed8f03ba5cfc3d5459f8977fa8ed/mqtt_esp8266-iot101/mqtt_esp8266-iot101.ino?at=master&fileviewer=file-view-default)

---

# mosquitto & paho by Python

MQTT通信の実例
Macで下記セットアップ

```
$brew install mosquitto
# mosquitto起動のため以下のどちらかを実行
# bg
$brew services start mosquitto
# fg
$/usr/local/opt/mosquitto/sbin/mosquitto -c /usr/local/etc/mosquitto/mosquitto.conf

$pip install paho-mqtt
```

---

## paho pub.py

[Pythonクライアントのチュートリアル](https://eclipse.org/paho/clients/python/)を真似つつ

```python:pub.py
import paho.mqtt.client as mqtt
import time

client = mqtt.Client()
client.connect('localhost', 1883, keepalive=60)
client.loop_start()
while True:
  client.publish('world/darai0512', 'test')
  time.sleep(1)
```

---

## paho sub.py

```python:sub.py
import paho.mqtt.client as mqtt

def on_connect(client, userdata, flags, respons_code):
  print('connected')
  client.subscribe('world/#')

def on_message(client, userdata, msg):
  print(msg.topic + ' ' + str(msg.payload))

client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect('localhost', 1883, keepalive=60)
client.loop_forever()
```

---

## Let's try MQTT

```
$python pub.py &
$python sub.py
world/darai0512 test
world/darai0512 test
...
```

---

[^1]: [MQTTのパケットを覗いてみた](https://mm011106.github.io/2014/12/20/mqtt-packets/)
[^2]: [MQTTにおけるトランスポート層セキュリティのトレードオフ](https://www.wolfssl.jp/wolfblog/2016/02/19/transport-level_security_tradeoffs_using_mqtt/)
[^3]: [初めての MQTT](https://gist.github.com/voluntas/89000a06a7b79f1230ab#topic)