i2cで気圧/気温センサBMP280の接続ができたら、いよいよ通信系の設定です。結構手こずったので、作った順にメモと一緒に記憶を整理します。
■通信系の設定
- 必要なnodeMCUモジュール:wifi
i2c経由でBMP280気圧/温度センサの情報を取得できるようになったら、次は通信系。通信系の設定は色々と手こずりました。複数のタイマーで接続完了を監視をしました。
- wifi接続をタイマーで監視、
- DHCPでIPをもらうのをタイマー監視、
- 後述するMQTTサーバへの接続をタイマー監視、 でようやく、温度と気圧をMQTTサーバへ送ることができます。
以下、参考にした情報を記載します。
■wifi.sta.status()
wifiの接続時にwifi.sta.status()の戻り値によって動作を確認しますが、戻り値(数値)が何を表しているのかが不明で、色々と探した情報です。戻り値”5”が"wifi.STA_GOTIP"のようですので、IPで通信するためには、DHCPでIPアドレスがもらえるまで待つ必要があります。
Return value
https://nodemcu.readthedocs.io/en/master/modules/wifi/#wifistastatus
The current state which can be one of the following:
wifi.STA_IDLE
wifi.STA_CONNECTING
wifi.STA_WRONGPWD
wifi.STA_APNOTFOUND
wifi.STA_FAIL
wifi.STA_GOTIP
hint:
node.restartをかけると、しばらく(5秒くらい)は"1"を返すが、その後"5"に変わる。ping も、restart後は返らないが、同様に5秒ほどすると返るようになる。ESP8266Devkit上のSWでのrestartでも同様。
2019/7/21
→5: wifi.STA_GOTIP
- wifiがつながっても、DHCPによるIP_addressの取得には時間がかかる → wifiの接続確認と、IPアドレス獲得の2つの監視が必要。
■MQTT
- 必要なnodeMCUモジュール:MQTT
MQTTとは、”MQTT:Message Queuing Telemetry Transport”。詳細は他のWEBを参照していただくとして、概要は以下の通り。
- データ発信端末(Publisher)、中継ノード(Broker)、データ受信端末(Subscriber)、の3つの要素からなる、軽量なデータ転送プロトコル
- Queuingそのものはしてくれない
当初、ESP8266DevkitにmqttのPubulisherを載せて、以下の参考に書いてある、Openなmqttサーバへの接続を試みたがうまく接続できず。結局、同じsubnet内のPCにBrokerを作って、同じsubnet内で閉じる系とすることとした。
→結局、加入しているプロバイダ側でhighport(port>1024?:詳細は不明、聞いても教えてくれない、、)をBlockするサービスを入れていて、それを解除しないと通信ができないことが後でわかった。Debugするためには、まずは全て手元でやるのが良いかもしれません。
下の方でMQTTサーバ(broker)の立て方に触れていますので、参考に。
参考:
nodeMCU/Luaを使って、BME280をMQTTで動かす例が載っている。 https://blog.alexellis.io/iot-nodemcu-sensor-bme280/
初めてのmqtt @GitHub https://gist.github.com/voluntas/89000a06a7b79f1230ab
■mqtt.client callback
- 必要なnodeMCUモジュール:mqtt
mqttの戻り値です。接続できなければResetして再度接続をするようにしました。
結局、同じsubnet内にbrokerを立ててやりましたが、実際には以下のreturn codeでerror code"-5"が戻って来て動かず、、。
Connection failure callback reason codes:
Constant | Value | Description |
---|---|---|
mqtt.CONN_FAIL_SERVER_NOT_FOUND | -5 | There is no broker listening at the specified IP Address and Port |
mqtt.CONN_FAIL_NOT_A_CONNACK_MSG | -4 | The response from the broker was not a CONNACK as required by the protocol |
mqtt.CONN_FAIL_DNS | -3 | DNS Lookup failed |
mqtt.CONN_FAIL_TIMEOUT_RECEIVING | -2 | Timeout waiting for a CONNACK from the broker |
mqtt.CONN_FAIL_TIMEOUT_SENDING | -1 | Timeout trying to send the Connect message |
mqtt.CONNACK_ACCEPTED | 0 | No errors. Note: This will not trigger a failure callback. |
mqtt.CONNACK_REFUSED_PROTOCOL_VER | 1 | The broker is not a 3.1.1 MQTT broker. |
mqtt.CONNACK_REFUSED_ID_REJECTED | 2 | The specified ClientID was rejected by the broker. (See mqtt.Client() ) |
mqtt.CONNACK_REFUSED_SERVER_UNAVAILABLE | 3 | The server is unavailable. |
mqtt.CONNACK_REFUSED_BAD_USER_OR_PASS | 4 | The broker refused the specified username or password. |
mqtt.CONNACK_REFUSED_NOT_AUTHORIZED | 5 | The username is not authorized. |
■MQTTのDebug環境整備
PCでmosquittoサーバ(broker)を立ち上げる
この辺りなどを参照
https://www.1ft-seabass.jp/memo/2015/07/13/windows-7-64bit-install-mosquitto/
windows10/64bitのCMD.exeを起動して、"mosquiito.exe -v" でbrokerを起動する
option ”-v” でbroker/subscriberからの情報がコンソールに出力される。(default port=1883)
この状態で、wifi-up して IP獲得が確認できたら mosquitto-serverに向かって接続をする。
AndroidでMQTT Dashboardを立ち上げてサーバにサブスクライバで覗きに行くとデータが見える
subscriberは、Androidの”MQTT Dashboard"とかなんでも良いですが、接続します。
mosquito.exe の入ったフォルダに、mosquitto_subというsubscribeのソフトも入っています。
立ち上げ方は、"mosquitto_sub.exe -v -h localhost -t "#"。
brokerを立ち上げた状態で、subscriberを立ち上げるとbroker側に接続された旨のメッセージがでるので、あとは自分の作ったアプリをDebugします。
ESPlorer.exeで
wifi_up.lua
とwifi-status.lua
でwifiに接続するwifiを起ち上げるスクリプト、wifiのstatusを確認するスクリプトをESP8266Devkitに入れておいて、dofileで叩くと、コマンドのように都度確認できます。
send-baro-temp.lua
を立ち上げて走らせると、サーバに向かってデータを投げる- wifiが立ち上がって、IPが取得できたあと、mqtt brokerにデータを投げる
memo:
★wifiのUpを確認するスクリプトをTryしたがうまくゆかず。 wifi.sta.status()が変わらない。
以下のスクリプトで、イベント待ちはできることは確認した。
wifi.eventmon.STA_CONNECTED 状態になったら function(T)をする、というもの。
wifi.eventmon.register(wifi.eventmon.STA_CONNECTED, function(T) print("\n\tSTA - CONNECTED".."\n\tSSID: "..T.SSID.."\n\tBSSID: ".. T.BSSID.."\n\tChannel: "..T.channel) end)
https://nodemcu.readthedocs.io/en/master/modules/wifi/#wifieventmon-module のあたりを参考に。
2019/7/28
■通信系の状態管理
通信はプロトコルの積み重ねで成立するので、上位は下部のプロトコルが接続されているかどうかを知る必要があります。wifi/IP/mqttの順で確立してゆくので、その状態を引き渡してゆきます。今回は、下部でエラーなり接続が切れた場合は、Full-resetするようにしました。
この状態遷移をEventDrivenでうまく書けなかったので、statusを持ちまわることで具現化。
status: 0初期化後の状態
status: 1wifiがUpした状態
status: 2mqttが接続完了した状態
2019/8/1
mqtt関連参考
https://iotbytes.wordpress.com/mqtt-with-nodemcu/
https://github.com/hobbyquaker/nodemcu-gpiomqtt/blob/master/gpiomqtt.lua
https://www.foobarflies.io/a-simple-connected-object-with-nodemcu-and-mqtt/
2019/8/6
色々と悩みつつLUAスクリプトの開発
そもそもLUA言語の考え方がよくわかっていなかったのですが、TRY&ERRORでやっちゃいました。先人のWEBを参考似、使い方については、https://nodemcu.readthedocs.io/en/master/ の中で検索しつつ作り上げました。上記URLには、それぞれの使い方も記載されているので、非常に参考になりました。スクリプトの名前があちこちにありますが、途中のスクリプトは掲載しませんが、最後に出来上がったスクリプトをご参考までに載せます。
■ wifi-reconnect-ok1.lua
- wifi.sta.getip()を待つ
wifi.eventmon.register(wifi.eventmon.STA_CONNECTED/DISCONNECTED)で表示されて、IPが払い出されるまでに6秒ほどかかる模様。なので、wifi.sta.getip()でIPをチェックしてから次に進まないとどこかでerrorになる。
error_max でIPがもらえない回数が超えると、reconnectする。
tmrは、MODEでtmr.ALARM_SEMIを設定すると、一度設定した割込みが入ると停止する、再度起動するためには、wifi.timer:start()が必要。
- wifi.sta.config(cfg)
cfg={}
cfg.auto=false でAutoConnectがFalseになる、=true でAutoConnect。Config RegisterにModeが記憶されるので、AutoConnectにすると、次にRebootから自動的に接続される。
■wifi-reconnect-mqtt-ok2.lua
ok1のUpdate版。mqttでpublishしている時に表示のためにnodeMCU基板上のLEDを点灯させる。
以下は、モバイルバッテリーにつないだESP8266Devkitをwifi経由でPCのMQTTBroker経由してAndroidのアプリでMQTT subcribeしたもの。有効数字?は少数以下2桁くらいありそう。止めてからキャプチャしたのでStatusは”Disconnected"となっています。つながっているときには、”Connected to 192.168.0.30"とかサーバ(MQTTBroker)のアドレスが表示されます。
Androidでbroker(参考)
MQTT Brokerをスマホにインストールして、スマホだけで覗けるようにもしてみました。
Androidに以下の手順でmosquittoをインストール
- APP storeで‘termux`をインストール(termuxはLinuxのターミナルソフト)
- apt update
- apt upgrade
- pkg install mosquitto
- mosquitto -v でbrokerを立ち上げる
192.168.0.3x(SmartPhoneを固定IP化して)で検証、動作確認しました。
これに加えて、同じSmartPhoneで同時にsubscribeするために、2画面にしてsubscriberを起動して動作を確認しました。OK!
2019/8/17
■wifi-reconnect-ok3.lua
PCのMQTT Brokerで、clean session=false
とすることでMQTT Brokerでのclean sessionをfalse, つまりsubscriber側が切れたら、brokerでpublishされたデータを保持してくれる。mqtt.Clientのパラメータで設定する。
(Brokerが落ちているとerror=-5で落ちて動かなくなるのが判明、、。要修正)→とりあえず、mqtt関連でofflineとか Mqtt failed. reason: -5、とかerrorが出たら、node.restart()するようにコード修正。
2019/8/18
なんだかわからないが、起動しても動かない、、。→i2cのケーブルが一本抜けていた。コードで以下の対処をした。
- BME280.setup()でinitializingの成功を確認、不成功の場合はnode.restartするようにした。i2cでも同様の仕組みを組み込んだ。→この場合、1秒間隔くらいでLEDが点滅する。(node.restartすると、D1pinのあたりの大きなLEDが点灯したのち、RST端子近くの小さなLEDが連続して点滅する。
- brokerが落ちた場合も、mqtt がofflineになるので、その時にもnode.restart。→この場合、15秒間隔くらいでLEDが点滅する。
2019/8/19
LUA開発環境が整ったところで、今度はLUAから見た時のinterfaceや使用したモジュールの使い方のポイントについて記載します。
nodeMCU v1.0 Pins
EPS8266Devkitの基板上に書かれたPinの名称(GPIO番号<以下の表では"ESP8266 Pin"の項目>)は、nodeMCUのポート(I/O index)番号とは異なります。以下がそのMapになります。i2cで使ったSCLとSDAのPinを表の右に示します。(i2cでは、この信号Pinと、電源:3V3、GNDの合計4本が必要です)
NodeMCU provides access to the GPIO (General Purpose Input/Output) and a pin mapping table is part of the API documentation.[17]
<figure></figure>
I/O index ESP8266 pin 備考 0 [*] D0:GPIO16 1 D1:GPIO5 SCL 2 D2:GPIO4 SDA 3 D3:GPIO0 4 D4:GPIO2 5 D5:GPIO14 6 D6:GPIO12 7 D7:GPIO13 8 D8:GPIO15 9 RX:GPIO3 10 TX:GPIO1 11 SD2:GPIO9 12 SD:3GPIO10 [*] D0 (GPIO16) can only be used for GPIO read/write. It does not support open-drain/interrupt/PWM/I²C or 1-Wire.
上記はhttps://en.wikipedia.org/wiki/NodeMCUから持ってきたものに、I/O indexに9-12と備考を追加したもの。2019/7/21
■ESP8266Devkitにi2cをつなげる
温度、気圧センサBMP280をi2cでESP8266Devkitに接続します。i2cはSCL(clock)、SDA(data)とGNDの3本(電源の供給も必要であれば4本)の信号線でデバイスを接続します。hardware編で記載したように接続します。まずはちゃんとnodeMCUからBMP280が見えるかどうか確認します。もちろん、nodeMCU上にi2cのソフトウェアモジュールが必要です。なければnodeMCUの再構築が必要となります。
■i2cの接続確認 "i2c_scanner.lua"
- 必要なnodeMCUモジュール:i2c
LUAで自動的にi2cの接続されているpin番号と、i2cのアドレスをチェックしてくれるプログラム。i2cの接続確認に非常に役に立ったので載せておきます。
https://www.esp8266.com/viewtopic.php?f=19&t=1049#p6198 です。
-- i2c_scanner.lua -- Based on work by sancho and zeroday among many other open source authors -- This code is public domain, attribution to gareth@l0l.org.uk appreciated. id=0 -- need this to identify (software) IC2 bus? gpio_pin= {5,4,0,2,14,12,13} -- this array maps internal IO references to GPIO numbers -- user defined function: see if device responds with ACK to i2c start function find_dev(i2c_id, dev_addr) i2c.start(i2c_id) c=i2c.address(i2c_id, dev_addr ,i2c.TRANSMITTER) i2c.stop(i2c_id) return c end print("Scanning all pins for I2C Bus device") for scl=1,7 do for sda=1,7 do tmr.wdclr() -- call this to pat the (watch)dog! if sda~=scl then -- if the pins are the same then skip this round i2c.setup(id,sda,scl,i2c.SLOW) -- initialize i2c with our id and current pins in slow mode :-) for i=0,127 do -- TODO - skip invalid addresses if find_dev(id, i)==true then print("Device found at address 0x"..string.format("%02X",i)) print("Device is wired: SDA to GPIO"..gpio_pin[sda].." - IO index "..sda) print("Device is wired: SCL to GPIO"..gpio_pin[scl].." - IO index "..scl) end end end end end
以下は、実際にESP8266Devkitに接続したBMP280の接続確認結果。 上記プログラムを、file名'i2c_scan.lua'でnodeMCUに登録(Save to ESPで書き込む)して、ESPloereの右ペインの最下段から”dofile('i2c_scan.lua')”で起動した結果です。I/O index 1,2 (GPIO4,5)につながった0x76番デバイスを見つけたことになります。私が使ったBMP280モジュールはdefaultでアドレスが0x76でした。BMP280を複数つなげるときには、アドレスを変更するため(同一i2cBUS上に同じアドレスは許容されない)にBMP280モジュール側でジャンパ等の対応をする必要があります。これにより0x77に設定できるようです。
dofile("i2c_scan.lua") -- Scanning all pins for I2C Bus device Device found at address 0x76 Device is wired: SDA to GPIO4 - IO index 2 Device is wired: SCL to GPIO5 - IO index 1
■i2cで接続されたBMP280から気温と気圧を得る
- 必要なnodeMCUモジュール:i2c、BME280
timer割り込みを使って、設定された時間(ここでは60sec)ごとに計測した温度と気圧を表示します。
★hints:
tmrモジュールはhttps://nodemcu.readthedocs.io/en/master/modules/tmr/ の記載(以下の注意書き)にあるように、2019年Q1(第一四半期)にI/Fが変更されていて、internet上に転がっている古いLUAプログラムを持ってきて動かすとエラーになることがあります。→私ははじめこれがわからず、随分苦労しました。nodeMCUのバージョンとLUAの組み合わせには注意が必要です。以下は、新しいnodeMCUで動作しますが、古いnodeMCUではエラーになります。
これをやっていた頃は2019の夏、丁度切り替わりの時期だったんですね。混乱しました。
■Attention
NodeMCU formerly provided 7 static timers, numbered 0-6, which could be used instead of OO API timers initiated with
tmr.create()
. After a long period of deprecation, these were removed in 2019 Q1.https://nodemcu.readthedocs.io/en/master/modules/tmr/ に記載があります。
tmr moduleは、static と dynamicがあって、staticは将来なくなる、と記載あり。で、例えば
--
NodeMCU custom build by frightanic.com branch: master commit: c16adb5dfb8c02b692034bbd553502765b9733cc SSL: false modules: adc,bme280,file,gpio,http,i2c,mqtt,net,node,pwm,rtctime,tmr,uart,wifi build created on 2019-07-20 23:15 powered by Lua 5.1.4 on SDK 2.2.1(6ab97e9) lua: cannot open init.lua
--
では、staticではエラーとなる。なので”dynamic”で動かす必要ある。
tmr.alarm([id/obj],interval,,,,,, と書いてあるので、[id/obj]を抜けばよいような気もする。→未確認。
2019/8/7
--- read-baro-temp-by-timer.lua --- alarmを使って定期的に気圧と気温を得る --- 2019/7/20 alt=50 scl=1 ---D1, GPIO05 sda=2 ---D2, GPIO04 interval = 60000 i2c.setup(0,sda,scl,i2c.SLOW) bme280.setup() print("## Start sensing with interval ", interval ,"msec ##") tmr.create():alarm(interval, tmr.ALARM_AUTO, function() P = bme280.baro() T = bme280.temp() print(string.format("QFE=%d.%03d Temp=%d.%02d", P/1000, P%1000, T/100, T%100)) end)
以下は、上記のプログラムの出力、60秒ごとに気圧と気温情報を取得して値を印字する。上記は、nodeMCUで、BME280モジュールが必要です。今回接続しているモジュールはBMP280ですが、nodeMCUのモジュール名はBME280です。nodeMCUがrestartされた時の搭載モジュール名にBME280がなければ、nodeMCUの再構築が必要です。
## Start sensing with interval 60000 msec ## QFE=1769.129 Temp=26.88 QFE=1769.143 Temp=26.89 QFE=1769.178 Temp=26.90 QFE=1769.205 Temp=26.91 QFE=1769.247 Temp=26.92 QFE=1769.288 Temp=26.94 QFE=1769.329 Temp=26.95
気圧が1769ヘクトパスカル、このBMP280の気圧の数値は明らかにおかしい。二個買ったのでもう一つで確認したところ、1000位の数値を出したので、そちらが正しい模様。この個体は不良品でした。