パスコンパスの日記

何か色々作るのが好きです

【台風監視】温度, 気圧, 湿度をm5StickCとUIFlowとAmbientで可視化してみました

きっかけ

 2019/10/12現在、台風19号が近づいてきています。私のTwitterのTLでは気圧計を自作されて気圧が下がるのを監視されている方が多くいらっしゃいました。この流れに乗りたいと思い、急遽作ってみました。

 使ったのはm5StickCです。ご存じない方のためにざっくりご説明するとm5StickCは画面、赤外LED、スピーカ、6軸センサ等々が載ったwifiにも繋がる人気のマイコンです。Arduino IDEC言語ベースでは結構情報がある*1と思うのですが、最近UiFLOWというブラウザ上でブロックプログラミングやmicro pythonを使える環境を導入してみたところだったので、今回はそちらでトライしてみました。

www.switch-science.com

※2019/10/13追記※
 micro pythonを最初から使う場合でしたら、norifumi(norifumi (@norifumi5001) | Twitter )さんの以下の記事が大変分かりやすいです。台風が去った後にこの記事を発見しました。普段から勢いで書いてしまいがちなのですが、あらかじめ調べるのも大切ですね...。

kitto-yakudatsu.com

※2019/10/15追記※
Kenta IDA(https://twitter.com/ciniml)さんがUiFLOWで直接Ambientのライブラリを再現されていらっしゃいました。 ブロックプログラミングでもここまでできるとは使う前は想像もしていませんでした。なかなか奥が深いです。

※追記ここまで※

目次

説明

 UiFLOW*2はIDでm5StickCやm5Stackというマイコンを紐づけてWifi経由でブラウザ上からプログラムを転送できる優れものの開発環境です。ブロックプログラミングやmicro pythonで書くことができます。今回はm5StickCのEnv Hat*3を使うため、Hatに対応しているBeta版の1.4.0を使いました。

ブロックプログラミング

温度、湿度、気温の表示

 最初にブロックプログラミングで値を取得して画面に表示させてみました。プログラムと動作の様子はこんな感じです。

 上記の様に用意されたブロックを繋ぐだけで簡単に欲しいデータを表示することができます。すごいです。また、画面の方は以下の様に指定をします。Labelをドラッグ&ドロップして配置しています。簡単なことを実行するのであればプログラムが全く分からない方でも直感的に使える素晴らしい環境だと思います。

f:id:yoichi_41:20191012014843p:plain

時間の表示

 m5StickCには腕時計の様に付けられるバンドが付属しています。時間も一緒に表示したいなと思い、情報を探していたら以下の五味さんのツイートを拝見しました。最初にntpサーバから時刻を取得し、その後内部のリアルタイムクロックで時を刻みます。

 ブロックを再現し、時刻も表示できました。ありがとうございました。ここまでできると次にはログをどこかに残したくなってきます。 m5StickCはせっかくwifiでネットにつながる機能もあるのでサーバにデータを上げること等できるのではないでしょうか。
 先日の技術書典で友人に買ってきてもらった以下の本にMQTTというやり方があることが書いてあり、それでやってみようと思ったのですが、Beta版ではMQTTブロックがまだサポートされていませんでした。別の方法を探します。また、以下の本はUiFLOWの導入法等も丁寧に書いてあり参考になりました。

booth.pm

micro pythonでAmbientと接続

 Ambientというサービスを使ってみることにしました。以下の公式のサイトを参考にしました。ブロックプログラミングではなく、micro pythonで書いています。参考にした記事ではUiFLOWではなくローカルから開発していたので少し書き方は変えています。 ambidata.io

クリックするとプログラムを表示します

# https://github.com/AmbientDataInc/ambient-python-lib/blob/master/ambient.py
# ここから下まで上記の内容をコピペしています。
class Ambient:
    def __init__(self, channelId, writeKey, *args):
        try:
            import urequests
            self.requests = urequests
            self.micro = True
        except ImportError:
            import requests
            self.requests = requests
            self.micro = False

        self.url = 'http://ambidata.io/api/v2/channels/' + str(channelId) + '/dataarray'
        self.channelId = channelId
        self.writeKey = writeKey
        if len(args) >= 2:
            self.userKey = args[1]
        if len(args) >= 1:
            self.readKey = args[0]

    def send(self, data):
        if isinstance(data, list):
            __d = data
        else:
            __d = [data]
        headers = {'Content-Type' : 'application/json'} if self.micro else {}
        r = self.requests.post(self.url, json = {'writeKey': self.writeKey, 'data': __d}, headers = headers)
        return r

    def read(self, **args):
        url = 'http://ambidata.io/api/v2/channels/' + str(self.channelId) + '/data'
        __o = []
        if hasattr(self, 'readKey'):
            __o.append('readKey=' + self.readKey)
        if 'date' in args:
            __o.append('date=' + args['date'])
        else:
            if 'start' in args and 'end' in args:
                __o.append('start=' + args['start'])
                __o.append('end=' + args['end'])
            else:
                if 'n' in args:
                    __o.append('n=' + str(args['n']))
                    if 'skip' in args:
                        __o.append('skip=' + str(args['skip']))
        if len(__o) > 0:
            url = url + '?' + '&'.join(__o)
        self.r = self.requests.get(url)
        return list(reversed(self.r.json()))

    def getprop(self):
        url = 'http://ambidata.io/api/v2/channels/' + str(self.channelId)
        if hasattr(self, 'readKey'):
            url = url + '?' + 'readKey=' + self.readKey
        self.r = self.requests.get(url)
        self.prop = self.r.json()
        return self.prop
# この上までがコピペです

from m5stack import *
from m5ui import *
from uiflow import *
import urequests
import hat

setScreenColor(0x111111)

hat_env0 = hat.get(hat.ENV)

# こちらを追記しました。
am = Ambient(※ここにAmbientの自分のチャンネルIDを貼り付けます, '※こちらには自分のライトキーを貼り付けます')

# ラベルの設定等です。(ブロックプログラミングで自動生成されます)
label0 = M5TextBox(3, 4, "Text", lcd.FONT_Default,0xFFFFFF, rotate=0)
label1 = M5TextBox(3, 57, "Text", lcd.FONT_Default,0xFFFFFF, rotate=0)
label2 = M5TextBox(38, 76, "hPa", lcd.FONT_Default,0xFFFFFF, rotate=0)
label3 = M5TextBox(4, 91, "Text", lcd.FONT_Default,0xFFFFFF, rotate=0)
label4 = M5TextBox(40, 109, "%", lcd.FONT_Default,0xFFFFFF, rotate=0)
label5 = M5TextBox(4, 125, "Text", lcd.FONT_Default,0xFFFFFF, rotate=0)
label6 = M5TextBox(43, 141, "oC", lcd.FONT_Default,0xFFFFFF, rotate=0)

datetime = None
time = None

def first_index(my_list, elem):
  try: index = my_list.index(elem) + 1
  except: index = 0
  return index

# タイマーを設定して表示しています。(追記箇所以外はブロックプログラミングで自動生成されます)
@timerSch.event('timer1')
def ttimer1():
  global datetime, time
  label0.setText(str(rtc.now()))
  label1.setText(str(hat_env0.pressure))
  label3.setText(str(hat_env0.temperature))
  label5.setText(str(hat_env0.humidity))

 #追記しました。Ambientにデータを送っています。gitのmain.pyからの引っ張ってきました。
  r = am.send({'d1': hat_env0.pressure, 'd2': hat_env0.temperature, 'd3': hat_env0.humidity})
# 以下の処理はなくても動いていますが、あった方が良いのかもしれません
# r.close()
  pass

# この辺りは五味さんの時間取得の処理部分です(自動生成)
label0.setText('')
try:
  req = urequests.request(method='GET', url='http://ntp-a1.nict.go.jp/cgi-bin/time')
  label0.setText(str(('ok:' + str((req.status_code)))))
  datetime = (req.text).split(' ')
  time = datetime[3].split(':')
  rtc.setTime(int(datetime[4]), first_index(['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], datetime[1]), int(datetime[2]), int(time[0]), int(time[1]), int(time[2]))
  label0.setText(str(rtc.now()))
except:
  label0.setText(str(('err:' + str((req.status_code)))))
timerSch.run('timer1', 100, 0x00)

 UiFLOWで複数のmicro pythonファイルもアップロードできるのでしょうか。よく分からなかったためAmbient.pyをimportするのではなくベタ書きで使ってしまいました。今回はそこまで長くなかったので問題ないですが、時間のある時に今後のために色々いじって調べてみたいです。
 下のツイートがデータを送ってPCの画面にAmbientのグラフを表示している様子です。左上が気圧、右が湿度、下が温度です。

 以下の図は2:35現在のグラフの様子ですが、だんだんと気圧が下がってきているのが観察できます。ノイズがちょっと気になりますね。また、温度は放熱を拾っている様であまり当てになりません。

f:id:yoichi_41:20191012023505p:plain

まとめ

 UiFLOWでもm5StickCとAmbientを使うと割と手軽にIoTチックなことができました。m5Stack系のデバイスは公式側の開発スピードが速く、どんどん情報が古くなってしまうのがデメリットでもあり魅力であると思います*4。流行り物好きな方にはおすすめです。
 上述した様に、ブロックプログラミングで完結する様な内容であれば初心者でも比較的手が出しやすいです*5
 台風にはお気を付けください。読んでくださりありがとうございました。

その他の記事

 普段はM5StickVとサーボを繋いだ2軸首振りマシンの記事を多く書いています。もしご興味ありましたら読んでいただけたら幸いです。

yoichi-41.hatenablog.com

*1:たなかまさゆきさんのブログです。Arduino言語で書かれています。

M5StickCにENV HAT(気温、湿度、気圧)をサーバーにアップする その1(基礎編) – Lang-ship

*2:UIFLOWはこちらのページです。M5Flow
使い方はこちら等参考になるかと思います。M5Stack UI Flowを使ってみよう

*3:温度、湿度、気圧が取れます M5StickC ENV Hat(DHT12/BMP280/BMM150搭載) - スイッチサイエンス

*4:ntpサーバにアクセスするブロックがバージョンで互換性が切れるということ等あった様です。

*5:ただArduinoやラズパイに比べてどうしてもまだ情報が少ないと思います