はじめに
_s__o_ です。
私事ですが、昨年マンションを引っ越しました。インターネット接続は、マンションの各戸に RJ-45 の口が付いているような、いわゆる「マンション共有型」タイプです (こちら の分類でいうところの「インターネット完備マンション」にあたります)。
メリットは利用料が安いことです。一方、マンションに引き込んだ光回線を共有設備の SW で各戸に分配しているため、回線速度が他戸の利用状況にかなり左右されます。
※ちなみに、マンションに引き込む光回線も、大本を (最大で) 32 分岐しているそうです (参考 )。今回の引っ越しを機に知りました。恐ろしい。。。
その「左右され具合」がなかなかに大きいので、いっそのこと「見える化」(not 可視化 but 数値化) してやろうと思いました。というわけで、Raspberry Pi 3 と AWS を使って、ちょろっと環境を作っていきます。
構成イメージ
それぞれの役割は下記のとおりです。
- Raspberry Pi 3
- Raspberry Pi 3 Model B で、中身は個人的な趣味で CentOS にしています。
- Python 2.7 で「(1) インターネット速度計測」、「(2) AWS IoT へのデータ配信」を実現します。 - AWS IoT Core
- Raspberry Pi 3 の配信データを Subscribe しています。 - DynamoDB
- AWS IoT Core のルールにもとづき、IoT が受信したデータを保管します。
準備
Dynamo DB
特記事項はありませんが、基本的なパラメータは下記としています。
- テーブル名 : internet_speed
- プライマリパーティションキー : client_id
→ クライアント名を格納します。クライアント名はこの後に登場する Python コードの中で定義しています。 - プライマリソートキー : timestamp_aws
→ 格納時のタイムスタンプ (unixtime) を格納します。タイムスタンプは後述の AWS IoT のルールの関数で出力します。
AWS IoT
こちら の記事を参照にしながら、エンドポイントを作成します。過程で証明書 & 秘密鍵が生成されるので、ダウンロードして Raspberry Pi 3 上に配置しておきます。
また、CA 証明書も必要になるので、こちら からダウンロードし、上記と同様に Raspberry Pi 3 上に配置しておきます。
上記を実施後、DynamoDB に連携するため、下記のようにルールを設定します。
SQL 部分をピックアップします。ポイントは下記の通りです。
- トピック名「internet_speed」から情報を取り出す。
- 「client_id」と「timestamp_aws」を関数で取得する。
- それ以外は微妙に名前を変えたりしながら取得する。
SELECT clientid() AS client_id, timestamp() AS timestamp_aws, unixtime As timestamp_device1, datetime AS timestamp_device2, down, up FROM 'internet_speed'
Raspberry Pi 3 (Python 2.7)
「速度計測用」と「AWS IoT へのデータ配信」に 2 つのコードを準備します。また、それらのコードを呼び出すラッパースクリプトを準備します。
速度計測用コード (speed_test.py)
処理として、主に下記を行います。
- Down 速度と Up 速度を計測する。
- タイムスタンプを取得する。
- 上記をまとめて json で出力する。
なお、「speedtest」というモジュールを使うため、事前に pip や easy_install でインストールする必要があります。インストール方法は こちら などを参照してください。
# -*- coding:utf8 -*-
# https://qiita.com/yokobonbon/items/67deb3fab84b0c0954e0
# https://qiita.com/hiroyuki827/items/3488a2b578de6777d2fc
import speedtest
import time
import datetime
import json
json_name = 'tmp.json'
def get_speed_test():
servers = []
stest = speedtest.Speedtest()
stest.get_servers(servers)
stest.get_best_server()
return stest
def get_now():
time_now = time.time()
return time_now
def test_speed(stest):
down_result = int(stest.download())
up_result = int(stest.upload())
return down_result, up_result
def command_line_runner():
stest = get_speed_test()
now_unixtime = get_now()
now_datetime = datetime.datetime.fromtimestamp(now_unixtime)
now_datetime_str = str(now_datetime.strftime('%Y/%m/%d %H:%M:%S'))
down_result, up_result = test_speed(stest)
f = open(json_name, 'w')
result = {'unixtime': (now_unixtime * 1000), 'datetime': now_datetime_str, 'down': down_result, 'up': up_result}
json.dump(result, f)
f.close()
if __name__ == '__main__':
command_line_runner()
データ配信用コード (publish_aws.py)
処理として、主に下記を行います。
- json ファイル (tmp.json) を AWS IoT に配信する。
なお、「AWS IoT Device SDK for Python」を使うため、事前に pip でインストールする必要があります。インストール方法は こちら などを参照してください。
また、コード中の ca_cert_path・key_path・cert_path の部分は、(上述の手順で配置した) CA 証明書・サーバ証明書・サーバ秘密鍵のパスを指定してください。
# -*- coding:utf8 -*-
# https://symfoware.blog.fc2.com/blog-entry-2224.html
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient
import time
import json
json_name = 'tmp.json'
# AWS IoT Info
clientName = 'lcrpi01' # クライアント名。AWS IoT の ルールの SQL で参照する
endpoint = '*****'
ca_cert_path = '*****'
key_path = '*****'
cert_path = '*****'
topic_name = 'internet_speed' # トピック名。AWS IoT の ルールの SQL で参照する
def command_line_runner():
# json load
f = open(json_name, 'r')
json_load = json.load(f)
f.close()
# For certificate based connection
myMQTTClient = AWSIoTMQTTClient(clientName)
myMQTTClient.configureEndpoint(endpoint, 8883)
myMQTTClient.configureCredentials(ca_cert_path, key_path, cert_path)
# Set MQTT Parameter
myMQTTClient.configureOfflinePublishQueueing(-1) # Infinite offline Publish queueing
myMQTTClient.configureDrainingFrequency(2) # Draining: 2 Hz
myMQTTClient.configureConnectDisconnectTimeout(10) # 10 sec
myMQTTClient.configureMQTTOperationTimeout(5) # 5 sec
# Publish to AWS IoT
myMQTTClient.connect()
myMQTTClient.publish(topic_name, json.dumps(json_load), 1)
if __name__ == '__main__':
command_line_runner()
ラッパースクリプト (internet_speed.sh)
上記の Python コードを呼び出すだけの単純なスクリプトです。これを cron で定期的に回します (30 分に 1 回など)。
# !/usr/bin/bash
cd /home/snishiyama/aws_iot
# 1. Speed Test
python ./speed_test.py
sleep 1
# 2. Publish to AWS IoT
python ./publish_aws.py
sleep 1
取得結果
こんな感じで取得できます。今後の展開を考えて、できるだけ生データで取得しているので、視認性が低いのはご容赦を。
おまけ
数値化とくれば次は可視化だと思います。AWS であれば、Elasticsearch Service & Kibana などが有名です……が、個人で利用するには些か高い (コストが)。
というわけで、簡単に可視化するため、Ambient というサービスとも組み合わせました (既に AWS は関係なくなってますが。。。)。
Ambient と連携するために、Raspberry Pi 3 に下記のコードを追加しています (publish_ambient.py)。例によって、事前に「ambient」モジュールを入れる必要があるため、こちら を参考にしながら導入しました。
# -*- coding:utf8 -*-
# https://ambidata.io/samples/temphumid/python/
import ambient
import json
json_name = 'tmp.json'
# Ambient Info
ambi_Channel_id = *****
ambi_write_key = '*****'
def convert_to_mbps(bps):
mbps = float(bps)/1000/1000
return mbps
def command_line_runner():
# json load
f = open(json_name, 'r')
json_load = json.load(f)
f.close()
# Adjust the value
down_mbps = convert_to_mbps(json_load['down'])
up_mbps = convert_to_mbps(json_load['up'])
# Publish to Ambient
msg = {'d1' : down_mbps, 'd2' : up_mbps}
ambi = ambient.Ambient(ambi_Channel_id, ambi_write_key)
r = ambi.send(msg)
return r
if __name__ == '__main__':
command_line_runner()
Ambient で描画したのが下記グラフです。期待したイメージ通りです。
……激しい荒ぶり方ですね。。。
まとめ
以上、Raspberry Pi 3 と AWS (IoT と DynamoDB) を使ってインターネットの回線速度を収集する方法でした。今まで IoT に触れたことがなかったため、こういった機会に経験できて良かったです。今後は他の項目を収集したり (せっかく Raspberry Pi を使っているので)、AWS の機能で何とか (低コストで) 可視化していきたいなあと思っています。
追記
その後、FTTH 開通しました。ちょうどタイミングが良く、ひいきにしているプロバイダが「IPoE 標準提供」を始めたため、期せずして IPoE デビューもできました。
肝心の Raspberry Pi 3 で計測したインターネット速度ですが……なぜか 100 Mbps で頭打ちです。他の端末 (PC やスマホ) で計測した場合は 300 Mbps を越えるのですが、なぜか Raspberry Pi 3 で計測した場合はそれが出てこない。明らかに原因は Raspberry Pi 3 なので色々調べてみたところ…… Raspberry Pi 3 の NIC は 100 Mbps までにしか対応していないという、哀しいオチが判明しました。
仕方がないのでギガイーサの USB NIC を Raspberry Pi 3 につないで、それで計測するようにしました。結果は下記の通りです。
※ちなみに、Raspberry Pi 3 の USB は USB 2.0 なので、USB NIC がギガでも、理論上の MAX は 480 Mbps となります。
まあ、300 Mbps は出ませんが、少なくとも以前に比べて改善は見られますので、とりあえず OK ということで。