前回 M5ATOMLiteのADCにCdSセルを接続し、値を読み取ることができました。今回は測定値をサーバ機Raspberry Pi3上のNodeREDに報告です。DHT11、BMP280と「既に通ってきた道」なので以下同文でOK、っと思ったら温度、湿度グラフのところにバグが。。。まあついでに修正。
※「MicroPython的午睡」投稿順 Indexはこちら
(末尾に今回実験に使用したMicroPythonスクリプトの全文を掲げました。なお使用しているMicroPythonは、ESP32用 “generic” ポートです。)
改訂したMicroPythonスクリプト
以前に作製したものを改訂しつつ使っておりますスクリプトの追加部分の説明です。
前回作成した LiteCds.py スクリプト、単独で使えばCdSセルの読み取りテストに使え、上位のスクリプトから呼べば測定用のモジュールに見えるものです。これをM5ATOMLite上のローカルファイルシステム上にコピーしてあります。そこで今回は、それをimportすれば良いっと。こんな感じ。
from LiteCds import LiteCDS
また、頻繁にとったり付けたりしている外付け回路の在、不在を管理するために変数を導入済でした。それに第3のデバイスとしてCDSを追加しました。今回は、3デバイス全て取り付けてあるのでこんな感じ。
DHT11_ON = 0x1 BMP280_ON = 0x2 CDS_ON = 0x4 extDevControl = BMP280_ON | DHT11_ON | CDS_ON
外付けデバイスの初期化コードは、extInit()に置く方針です。CDSについてはデバイスのインスタンスを作成するだけ。
def extInit(): global DHT11, BMP280, CDS if (extDevControl & DHT11_ON) != 0: DHT11 = LiteDHT11() print("Device: DHT11, active.") if (extDevControl & BMP280_ON) != 0: BMP280 = LiteBMP280() BMP280.setup() print("Device: BMP280, active.") if (extDevControl & CDS_ON) != 0: CDS = LiteCDS() print("Device: CDS, active.")
測定の実体は、asyncio使って周期的に呼び出されるタスク内から呼ばれる以下の関数です。CDS追加したところが以下に。
def makeOBJ(num): global DHT11, BMP280, CDS workDic = dict() if (extDevControl & DHT11_ON) != 0: tpl = DHT11.measure() workDic['TEMP'] = tpl[0] workDic['HUMI'] = tpl[1] if (extDevControl & BMP280_ON) != 0: tpl = BMP280.measure() workDic['BMP280_T'] = tpl[0] workDic['BMP280_P'] = tpl[1] if (extDevControl & CDS_ON) != 0: tpl = CDS.measure() workDic['CDS_RAW'] = tpl[0] workDic['A'] = num workDic['B'] = LiteNet.currentTIME() return ujson.dumps(workDic)
CdSセルの測定については、生のADC測定値、その移動平均、電圧換算、抵抗換算と計算していますが、照度のような人に分かり易い単位に換算できるわけでないので、NodeREDには、生のADC値をそのまま報告することにいたしました。暗い(入射する光のエネルギーが小さい。人の目に感じる周波数以外にも感度があるので明るさとは言い切れない)と小さくなり、明るいと大きくなる数字。12ビットのADCを余裕な9ビットで使っているので0-511の範囲です。
NodeREDの修正
NodeRED側の修正は、まずCdSに対する追加です。
-
- ダッシュボード上に4番目のグラフを追加。CdSセル生測定値用とする
- JSONオブジェクトを仕分けるためにfunctionノードを改造、出口を+1(CdS用)した。
さらに以下、2点もBUGがありFIXしました。多分、グラフをコピペしたときに間違えたです。コピペは危険。要注意。
-
- DHT11HUMIグラフには複数系列の入力ないのに凡例アリになっていた、凡例ナシに改めた
- TEMPグラフには、DHT11からの温度とBMP280からの温度の2系列が入る筈だったのに、凡例ナシになっていたのを凡例アリに改めた
- 上記のTEMPグラフの構造上、2つ別Topicのmsgが到来しないとならないが、functionノードでの仕分けをバグっていて1方しか到来していなかったので修正(この件でも出口を+1。)
やっつけでご乱心な作業の結果であります。修正したM5ATOMLite用のフローの全体が以下に。
動作結果
NodeREDダッシュボード上ATOMLiteタブの、測定開始(M5ATOM上のスクリプト起動)からほぼ1時間を経過した時点での、接続デバイスの測定値のグラフ表示は以下です(PC上のChromeからNodeREDが走っているRaspberry Pi3機に接続してみています。)
左上のCDSとあるのがCdSセルの生測定値です。開始後急に0まで落ちているのは、そこで黒のフェルトをセンサに被せてほぼ真暗にしたためです。その直後にフルスケールまで振れているのは、LED照明をCdSセルの直上数cmから照らして明るくしたためです。その後、室内の通常の照明にもどった後は安定した値を示しています。
BUG-FIX後の温度センサのグラフは、DHT11とBMP280の2系統が表示されるようになりました。ほとんど同じ場所で温度を測っているので、測定値の差は極めて微妙。まあ乖離していたらそれはそれで問題だけれども。
MicroPython的午睡(54) ATOMLite、ADCにCdSセルを接続 へ戻る
MicroPython的午睡(56) ATOMLite、パッシブ・ブザー接続 へ進む
実験に使用したMicroPythonコード、最上位スクリプト全文
import sys import ujson import uasyncio from LiteOnBoard import LiteOnBoard from LiteNetwork import LiteNetwork from LiteDHT11 import LiteDHT11 from LiteBMP280 import LiteBMP280 from LiteCds import LiteCDS ledColor = (0, 0, 0) OnBoardDevice = LiteOnBoard() LiteNet = LiteNetwork() DHT11 = None BMP280 = None CDS = None DHT11_ON = 0x1 BMP280_ON = 0x2 CDS_ON = 0x4 extDevControl = BMP280_ON | DHT11_ON | CDS_ON def colorChange(msg): global ledColor if msg=="R": ledColor = (255, 0, 0) elif msg=="G": ledColor = (0, 255, 0) elif msg=="B": ledColor = (0, 0, 255) def callbackSub(topic, msg): topicS = topic.decode('utf-8') msgS = msg.decode('utf-8') print(topicS, msgS) if topicS=="ATOMLite/Color": print("ATOMLite/Color Message: ", msgS) colorChange(msgS) if topicS=="ATOMLite/Settings": msgJ = ujson.loads(msgS) print("ATOMLite/Settings: ") for item in msgJ: for k,v in item.items(): if k=="key": ky = v if k=="value": vl = v print(ky, " = ", vl) def makeOBJ(num): global DHT11, BMP280, CDS workDic = dict() if (extDevControl & DHT11_ON) != 0: tpl = DHT11.measure() workDic['TEMP'] = tpl[0] workDic['HUMI'] = tpl[1] if (extDevControl & BMP280_ON) != 0: tpl = BMP280.measure() workDic['BMP280_T'] = tpl[0] workDic['BMP280_P'] = tpl[1] if (extDevControl & CDS_ON) != 0: tpl = CDS.measure() workDic['CDS_RAW'] = tpl[0] workDic['A'] = num workDic['B'] = LiteNet.currentTIME() return ujson.dumps(workDic) async def reportStatus(period_s): while True: LiteNet.client.publish("ATOMLite/Status", "ATOMLite Running: " + LiteNet.currentTIME()) await uasyncio.sleep(period_s) async def sendJson(period_s): sendCounter = 0 while True: jsonSTR = makeOBJ(sendCounter) LiteNet.client.publish("ATOMLite/Json", jsonSTR) sendCounter += 1 await uasyncio.sleep(period_s) async def ledAndUserButton(period_s): global OnBoardDevice ledFlag = True buttonFlag = False while True: if ledFlag: OnBoardDevice.M5ATOMLiteLED(ledColor) ledFlag = False else: OnBoardDevice.M5ATOMLiteLED( (0, 0, 0) ) ledFlag = True if OnBoardDevice.M5ATOMLiteUserButton(): print("Button ON") LiteNet.client.publish("ATOMLite/Button", "Button ON") buttonFlag = True elif buttonFlag: buttonFlag = False LiteNet.client.publish("ATOMLite/Button", "Button OFF") await uasyncio.sleep(period_s) async def mainLoop(period_ms): global OnBoardDevice taskStatus = uasyncio.create_task(reportStatus(60)) taskJson = uasyncio.create_task(sendJson(60)) taskATOMhard = uasyncio.create_task(ledAndUserButton(1)) while( OnBoardDevice.buttonCounter < 6 ): LiteNet.client.check_msg() await uasyncio.sleep_ms(period_ms) try: taskStatus.cancel() except: print("Exception at taskStatus:", sys.exc_info()[0]) try: taskJson.cancel() except: print("Exception at taskJson:", sys.exc_info()[0]) try: taskATOMhard.cancel() except: print("Exception at taskATOMhard:", sys.exc_info()[0]) LiteNet.client.publish("ATOMLite/Button", "N/A") LiteNet.client.publish("ATOMLite/Status", "ATOMLite: out of service.") LiteNet.close() print("disconnect.") def extInit(): global DHT11, BMP280, CDS if (extDevControl & DHT11_ON) != 0: DHT11 = LiteDHT11() print("Device: DHT11, active.") if (extDevControl & BMP280_ON) != 0: BMP280 = LiteBMP280() BMP280.setup() print("Device: BMP280, active.") if (extDevControl & CDS_ON) != 0: CDS = LiteCDS() print("Device: CDS, active.") def main(): if not LiteNet.do_connect(): print("ERROR: WiFi connection.") sys.exit(1) LiteNet.initRTC() if not LiteNet.connectMqttBroker(callbackSub): print("ERROR: MQTT Broker connection.") sys.exit(1) extInit() uasyncio.run(mainLoop(1000)) if __name__ == "__main__": main()