はじめに
- Elixirを楽しんでいますか
-
Nerves Livebookを用いて、AHT20というモジュールを用いて温度・湿度を測ってAzureでグラフ表示したいとおもいます
- もしAHT20 もしくは代替のものがご用意いただけない場合にはランダムな値でも送ってみましょう
必要なもの
- Azureアカウント
- Raspberry Pi 4
- microSDカード 16GB
- ACアダプター
- Grove AHT20 I2C温度および湿度センサー 工業用グレード
-
Raspberry Pi用Grove Base Hat
- 手先が不器用ですからな私でもSeeedさんの製品を使うことで、IoTを楽しめます
- もし同じものもしくはなにかを測定できる代替のモノをお持ちではない方は、ランダムな値でグラフを書いてみましょう
参考
- くらでべ IoT 10 分間クッキング
- Device - Send Device Event
-
Azure IoT HubにHTTP POSTでメッセージを送信するメモ
- @tmitsuoka0423 さん
- こちらの記事にとても助けていただきました
- ありがとうございました!
- こちらの記事のおかげでIoT HubにおけるHTTPSにたどり着くことができました
Nerves Livebook
- 手前味噌の「Nerves Livebook Firmwareを使って温度・湿度のグラフをかいてみる」を参考に、microSDカードにNerves Livebookをこんがり焼いて、電源ONするところまで進めてください
Azure IoT Hub
- ポータル画面 https://portal.azure.com/#home から、
リソースの作成 > IoT Hub
と進んで作成してみましょう - 必須入力項目を入力して、選択するところは基本はデフォルトのままにしておいて、価格とスケールティアは
F1
を選びました
- でも飲んで待ちましょう
IoTデバイス
- Azure IoT Hubができあがったら、IoTデバイスを追加します
- デバイスの追加
- デバイスIDは
livebook
と名付けました
SASトークンをつくる
- このあとHTTPSでIoT Hubにデータを送ります
- その際に必要となるのがSASトークンです
注意喚起
-
@matsujirushi さんからとっ〜〜〜ても大事なコメントをいただきました
- ありがとうございます!
- https://qiita.com/torifukukaiou/items/f630fb9b4caee8ead094#comment-2e0e68b904cd64017de0
- この記事に書いてあることをそのままやるといずれ有効期限を迎えてしまうとのことです
- Azureの料金が気になってすぐに止めてしまったので気づきませんでした
参考
- Shared Access Signature とセキュリティ トークンを使用して IoT Hub へのアクセスを制御する
- az iot hub generate-sas-token
- Docker コンテナーで Azure CLI を実行する方法
Dockerを使ってSASトークンをつくってみました
$ docker run -it mcr.microsoft.com/azure-cli
bash-5.1# az login
bash-5.1# az iot hub generate-sas-token --hub-name {IOT_HUB_NAME}
- 私の場合は以下のようになります
bash-5.1# az iot hub generate-sas-token --hub-name iot-hub-awesome
- 実行結果をメモしておきます
-
"SharedAccessSignature sr=xxxx"
を使います
Run!!!
- Nerves Livebookで操作をします
-
http://nerves.local にアクセスして、Authenticationは
nerves
を入力 - あとは右上の
New notebook
からElixirのプログラミングを楽しみましょう
AHT20から温度・湿度を取得するプログラム
defmodule Aht20.Reader do
alias Circuits.I2C
@i2c_bus "i2c-1"
@i2c_addr 0x38
@initialization_command <<0xBE, 0x08, 0x00>>
@trigger_measurement_command <<0xAC, 0x33, 0x00>>
@two_pow_20 :math.pow(2, 20)
def read do
{:ok, ref} = I2C.open(@i2c_bus)
I2C.write(ref, @i2c_addr, @initialization_command)
Process.sleep(10)
I2C.write(ref, @i2c_addr, @trigger_measurement_command)
Process.sleep(80)
ret = I2C.read(ref, @i2c_addr, 7)
I2C.close(ref)
value(ret)
end
defp value({:ok, val}), do: {:ok, convert(val)}
defp value(_), do: :error
defp convert(<<_, raw_humi::20, raw_temp::20, _>>) do
humi = Float.round(raw_humi * 100 / @two_pow_20, 1)
temp = Float.round(raw_temp * 200 / @two_pow_20 - 50.0, 1)
{temp, humi}
end
end
- AHT20もしくは代替のセンサーのご用意がない場合はランダムな値を返すようにしてみましょう
defmodule Aht20.Reader do
def read do
{:ok, {Enum.random(250..300) / 10, Enum.random(600..800) / 10}}
end
end
IoT Hubへデータを送り込むプログラム
f = fn ->
url = 'https://iot-hub-awesome.azure-devices.net/devices/livebook/messages/events?api-version=2020-03-13'
:inets.start
:ssl.start
{:ok, {temp, humi}} = Aht20.Reader.read()
{:ok, datetime} = DateTime.now("Etc/UTC")
time = datetime |> DateTime.to_unix()
json = Jason.encode!(%{value: %{temperature: temp, humidity: humi, time: time}}) |> String.to_charlist()
request = {url, [{'Authorization', 'SharedAccessSignature sr=xxx'}], 'application/json', json}
IO.inspect(request)
:httpc.request(:post, request, [], [])
end
f.()
-
SASトークンをつくる
のところで作ったトークンを指定します - urlは以下の形式です
https://fully-qualified-iothubname.azure-devices.net/devices/{id}/messages/events?api-version=2020-03-13
- https://docs.microsoft.com/ja-jp/rest/api/iothub/device/send-device-event
{:ok,
{{'HTTP/1.1', 204, 'No Content'},
[
{'date', 'Sat, 04 Sep 2021 05:56:41 GMT'},
{'server', 'Microsoft-HTTPAPI/2.0'},
{'vary', 'Origin'},
{'content-length', '0'},
{'x-ms-request-id', '1f8cfa57-c449-446d-bca0-0d11c02965f4'}
], []}}
- こんな応答が返ってきたら成功です
定期的にIoT Hubへデータを送り込むプログラム
defmodule Ticker do
use GenServer
def start_link(state) do
GenServer.start_link(__MODULE__, state, name: __MODULE__)
end
def init(state) do
:timer.send_interval(1000 * 20, self(), :tick)
{:ok, state}
end
def handle_info(:tick, state) do
send()
{:noreply, state}
end
defp send do
url = 'https://iot-hub-awesome.azure-devices.net/devices/livebook/messages/events?api-version=2020-03-13'
:inets.start
:ssl.start
{:ok, {temp, humi}} = Aht20.Reader.read()
{:ok, datetime} = DateTime.now("Etc/UTC")
time = datetime |> DateTime.to_unix()
json = Jason.encode!(%{value: %{temperature: temp, humidity: humi, time: time}}) |> String.to_charlist()
request = {url, [{'Authorization', 'SharedAccessSignature sr=iot-hub-awesome.azure-devices.net&sig=XSUR%2BX3tP9Em%2FVqQx5Q%2FwSIeq5qUiYGyD8BG3Bog51g%3D&se=1630737793&skn=iothubowner'}], 'application/json', json}
IO.inspect(request)
:httpc.request(:post, request, [], [])
end
end
Ticker.start_link([])
- 20秒ごとにセンサーからの値取得、IoT Hubへの送信を行っています
- このまましばらく放置しておきます
-
でも飲んで待っていると、デバイスからのデータが届いていることがわかります
- 図はIoT Hubの概要
Time Series Insight
- Time Series Insightでグラフ表示をしてみます
- 「作成」を迷わず押して
- あとはカンで値を入力して、Time Series Insightをデプロイしましょう
- でも飲んでデプロイが終わって、「リソースに移動」
- さらに「TSIエクスプローラーに移動」
- そうするとこんな画面になります
-
null
のところを触って、あとは雰囲気でvalue.humidity
とvalue.temperature
にチェックをいれましょう - そうすると以下のようなグラフが表示されます
- ランダム値を送り込むプログラムでの結果です
- 温度・湿度なのでそれほど激しくはうごきません
Wrapping Up
- Enjoy Elixir
最後の最後に
- Nervesとは、ElixirでIoTを楽しめるナウでヤングでcoolなすごいヤツです
- ここでNervesJPの紹介です
- 月1回程度、ワイワイガヤガヤ オンラインで集まっています
- Nervesに興味をもっていだけましたらぜひぜひご参加ください
- 愉快なfolksたちがあなたの参加を待っています
- れっつじょいな〜す
- https://join.slack.com/t/nerves-jp/shared_invite/enQtNzc0NTM1OTA5MzQ1LTg5NTAyYThiYzRlNDRmNDIwM2ZlZTJiZDc1MmE5NTFjYzA5OTE4ZTM5OWQxODFhZjY1NWJmZTc4NThkMjQ1Yjk
- ぜひぜひSlackにご参加ください
-
ブレッドボードとLチカは今回の記事とはなんら関係ありません ↩