Display the graph on the iPad
ESP8266 のセンサーを iPad でグラフ表示しました。 2016.08.08
リアルタイムにグラフ表示し変化をモニターできます。
日本語音声で「温度は27度です」とか話してくれたり 測定時の動作状況を
サウンドで教えてくれたり 離れていても耳で知ることができます。
音声やサウンドでお知らせしてくれるのは なかなか楽しいですよ。
iOSデバイス上で動作する統合開発環境の Pythonista 3 で製作しました。
機能:
1. ESP8266 BME280 温度、湿度、気圧センサーのデータを受信します。
2. 数値表示:スイッチをオンにすると日本語音声で数値を発音します。
_ 温度、湿度、気圧 の必要なものを聞く事ができます。
3. グラフ :現在測定値は右端に表示し リアルタイムに左に移動します。
4. 測定間隔:左下のスイッチで「秒」「分」「時」「日」を選択します。
_ 例:[分]は「3分間隔で測りますね」「今33分です」と発音。
5. 動作状態:「時」スイッチの上にあるトグルスイッチ。
_ オンにすると測定時(受信時)にサウンドが鳴ります。
準備:
1. ESP8266 と BME280 を I2C(4本) で接続。
_ ESP8266 : WeMos D1 mini with TELEC:SDA=D6(12), SCL=D7(13)
2. iPad 3 iOS 9.3.3 + Pythonista 3
3. iMac 27int OSX 10.8.5
4. パネルのデザインは画像を参照願います。
ESP2866 と iPad の信号の流れ:
1. iPad :起動時に ESP8266 へ モード信号 (秒:”1″) を送信する。
2. ESP8266:それを受けて 6秒間隔モードに設定する。
3. ESP8266:iPadへ 6秒モードを送信する。
4. iPad :ESP8266 からの返事(“1”)により スイッチ(秒)を点灯(緑)。
5. iPad :EPP8266 からのデーターを受信し表示を繰り返します。
6. iPad :測定間隔を変更:スイッチでモードをEP8266 へ送る。
7. ESP8266:測定間隔を切り替え モードの返事を iPad へ送る。
8. iPad :ESP8266 からの返事により スイッチを緑に点灯します。
9. ESP8266:設定された時間間隔で 測定値を iPad へ送り続けます。
.
🍎 グラフを Ui 画面に表示する方法:
1. Pythonモジュール は import matplotlib.pyplot as plt を記述する。
2. 配列された x,y のデーターを用意する。例:x[1,2,3,4],y[3,2,5,7]
3. UI 編集で imageview (画像)の枠を用意する。名前は _panel とする。
4. v = ui.load_view(‘_panel’) で UI を読み込む。名前は v となる。
5. plt.plot(x,y) で Console へ抽画。( Coonsoleの表示は plt.show() )
6. plt.savefig(‘pic.png’) で Console の画像をファイルに保存する。
7. v[‘imageview1’].image = ui.Image.named(‘pic.png’)
_ で保存された画像を UI の imageview (v) に表示する。
メモ:Python の グラフ抽画は Console に表示(plt.show)されます。(5)
_ iPad は imageview に表示します。 (7)
.
🍎 日本語音声を出力する方法:
1. Pythonモジュール は import speech を記述する。
2. speech.say(‘こんにちわ’)
.
🍎 サウンド(sound) を使用する方法:あのマックのコインの音です。
1. Pythonモジュール は import sound を記述する。
2. sound.play_effect(‘Coin_1’)
.
🍎 スイッチ(Switch) の使用方法:
1. スイッチをオン( Action ) すると スイッチ操作音を出す。
_ def son(s):sound.play_effect(‘Click_1’)
_ UI の編集で Switch の「Action」項目に「son」と記述する。
_ Switch が 押されると def son に飛んで来て記述内容が動作します。
.
参考:
warning about too many open figures:pyplotで作ったデータをcloseする事。
感想:
1. Pythonista 3:7月末から始め 知識不足が多々ある中で作りました。
_ スクリプトは不十分な部分が多いと思いますのでご容赦を。
_ 製作方法:ほとんどネットで調べて作りました。
_ 図書館や本屋に行きましたが 使用できる場面は かなり少なかったです。
2. 問題点:
_ 耐久テストが少ない為に 時間経過でクラッシュするかもしれません。
_ おそらく Matplotlib の pyplot で画像を多数作る時に問題があると予想
_ します。グラフ範囲の xlim,ylim は影響が大きく使用しない方が良い。
_ :
_ グラフを見ると言う事は iPad の電源を入れっぱなしにする事になる。
_ そうすると 充電しながらでも電源が持たないかもしれません。
3. 使用感:音声やトーンは楽しい。
_ グラフに興味はある物の 日常ではグラフを見つめ続ける事は少ない。
_ 1行で簡単に音声が追加できますので楽しいですね。
_ 音声によるアクションや説明は解り易く面白いものです。
_ ただし 音声は多様すると うるさく なるかもしれません。
4. Pythonista ( Python ) の命令:非常に多く大変ですが 凄さに驚くばかりです。
_ 複数のグラフを重ねて表示とか目盛りを別々表示とかの機能が凄い!
_ 今回のスクリプトよりも もっと良い記述の方法があるかと思います。
_ iPad の UI 機能を駆使する事がポイントですが 現在 情報が少ないです。
_ 特に高速グラフィックの方法はなかなか難しそうです。
Pythonista 3:スクリプト
# ipad temp,humi,pres display : 2016.08.08 macsbug # rev 1 : 2016.08.11 macsbug # phthonista 3, ios 9.3.3 import socket import ui import numpy as np import matplotlib.pyplot as plt import time import Image import sys import datetime import speech import os import sound import math from matplotlib.ticker import * def udp_transceiver(s): # tx.sendto(bytes(s, 'utf-8'), (HOST, PORT)) def udp_receiver(s): # global d1, d2, temp, humi, pres, mode, sw d1 = str(rx.recv(32), 'utf-8') # d1 = 't/1/0/0' d2 = d1.split('/') # b/23.45/45.65/1008.235 mode = d2[0] # mode temp = d2[1] # temp humi = d2[2] # humi pres = d2[3] # pres if mode == 't':sw = d2[1] # time interval switch def sw1(s): udp_transceiver('1') # sw1 def sw2(s): udp_transceiver('2') # sw2 def sw3(s): udp_transceiver('3') # sw3 def sw4(s): udp_transceiver('4') # sw4 def son(s): sound.play_effect('Click_1')# switch sound def all_of_switch(s): # button off v['button1'].background_color = of # 1 v['button2'].background_color = of # 2 v['button3'].background_color = of # 3 v['button4'].background_color = of # 4 def bot(bot1, bot2, bot3, bot4): # all_of_switch('') # v[str(bot2)].background_color = on # speech.say(str(bot4)) # speech.say('現在' + str(bot1) + str(bot3) + 'です') def audio(a1, a2, a3): speech.say(str(a1) + str(a2) + str(a3)) def clocktime(s): # global tm, clock # d = datetime.datetime.today() # clock time tm[4] = d.day; tm[3] = d.hour # clock time tm[2] = d.minute; tm[1] = d.second # clock time v['textfield5'].text = str(d)[0:19] # datetime display clock = tm[int(sw)] # clock time select # start ------------------------------------------------------- # panel setup --------------------------# v = ui.load_view('_BME_panel') # panel install v.background_color = '#d3dad7' # panel color on = '#b0ffb7';of = '#c5c5c5' # sw on/off color v['label7'].text = '' # axis y v['textfield5'].text = 'start' # all_of_switch('') # all sw off v.present('sheet') # panel in view # udp setup HOST, PORT = '192.168.100.47', 7000 # ESP8266 ip, port rx = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) rx.bind(('', 9000)) # ipad tx port tx = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # variavle setup -----------------------# speech.say('こんにちわ') # hello temp, humi, pres, clock = 0, 0, 0, 0 # tm = [0, 0, 0, 0, 0] # clock mode, d1 = 'a', 't/1/0/0' # mode t/d, rx data d2 = [0, 0, 0, 0] # mode,temp(sw),humi,pres sw = 1 # time interbal sw x1 = 60 # x max sound_onoff = 'off' # sound on/off sel # udp check ----------------------------# udp_transceiver('1') # ipad -> ESP8266 udp_receiver(''); c = 0 # return check ret = mode + sw # return check while (ret) != 't1': # return check v['textfield5'].text = str(ret) # ret signal udp_transceiver('1') # re tx udp_receiver(''); c = c + 1 # re rx vs = '接続できませんでした' # fail if c > 100: speech.say(str(vs)) # fail count x1 = 60 # sw 1 v['button1'].background_color = on # sw 1 lamp on sound.play_effect('Coin_1') # rx ok # receive data check -------------------# udp_receiver('') # first receive while mode != 'd' # return check v['textfield5'].text = str(d1) # rx data display if mode == 'd': # mode d check msg = str(mode) + ' : error' # error message v['textfield5'].text = str(msg) # error message udp_receiver('') # re mode ck v['textfield5'].text = str(d1) # rx data sound.play_effect('Coin_2') # rx ok ty = [temp for i in range(0, 60)] # temp array setup hy = [humi for i in range(0, 60)] # humi array setup py = [pres for i in range(0, 60)] # pres array setup # graph set up -------------------------# pd = 5 # pres diff ph = int(float(pres)) + pd # pres hi pl = int(float(pres)) - pd # pres lo xp = [i for i in range(0, 60)] # x_line fig = plt.figure() # graphics setup ax1 = fig.add_subplot(111) # temp, hum ax2 = ax1.twinx() # pres axis ax1.grid(True); ax2.grid(True) # temp,humi,pres axis # ============================================================== # main routine while True: udp_receiver('') # receive clocktime('') # clock time # ------------------------------------------------------------ # switch mode : time interval setup if mode == 't':sound.play_effect('Coin_1') if mode == 't' and sw == '1': bot(tm[1], 'button1', '秒', '6秒間隔で測ります') x1 = 60; xp = [i for i in range(0, x1)] if mode == 't' and sw == '2': bot(tm[2], 'button2', '分', '3分間隔で測りますね') x1 = 60; xp = [i for i in range(0, x1)] if mode == 't' and sw == '3': bot(tm[3], 'button3', '時', '1時間間隔で測りますよ') x1 = 24; xp = [i for i in range(0, x1)] if mode == 't' and sw == '4': bot(tm[4], 'button4', '日', '8時間毎に測りますね') x1 = 7; xp = [i for i in range(0, x1)] if mode == 't': plt.xlim(0, x1) # x axis # ------------------------------------------------------------ # data mode : digital display, sound on/off switch if mode == 'd': if sound_onoff == 'on': sound.play_effect('Ding_3') v['textfield1'].text = temp; ti = round(float(temp)) v['textfield2'].text = humi; hi = round(float(humi)) v['textfield3'].text = pres; pi = round(float(pres)) v['textfield4'].text = str(clock) if v['switch1'].value: audio('温度は', ti, 'どです') if v['switch2'].value: audio('湿度は', hi, '%です') if v['switch3'].value: audio('気圧は', pi, 'ヘクトパスカルです') if v['switch4'].value: sound_onoff = 'on' else:sound_onoff = 'off' # ----------------------------------------------------------- # graphics ty.pop(0); hy.pop(0); py.pop(0) # left ty.append(temp); hy.append(humi); py.append(pres)# add data ph = int(float(pres)) + pd; pl = int(float(pres)) - pd ax1.plot(xp, ty, color='#2cff00', lw='1') # temp graph ax1.plot(xp, hy, color='#b780ff', lw='1') # humi graph ax2.plot(xp, py, color='#2be3ff', lw='1') # pres graph ax2.plot(x1, ph, color='#ffffff', lw='0') # pres hi ax2.plot(x1, pl, color='#ffffff', lw='0') # pres lo plt.savefig('_a.png') # save the graph on the consolen v['imageview1'].image = ui.Image.named('_a.png') # imageview plt.cla() # clear axis plt.close() # close a figure window fig = plt.figure() # ax1 = fig.add_subplot(111) # ax2 = ax1.twinx() # ax1.grid(True) # ax2.grid(True) # #------------------------------------------------------------
ESP8266 スケッチ:
// BME280 Temo,Humi,Pres Sensor: ESP8266 & iPad : 2016.08.01 macsbug // ESP8266 : WeMos D1 mini with TELEC // UDP communication // ESP8266(ip:192.168.100.47,port:9000) transmit ---| // ipad (ip:192.168.100.22,port:9000) receive <--| // // ipad (ip:192.168.100.22,port:7000) transmit ---| // ESP8266(ip:192.168.100.47,port:7000) receive <--| #include <ESP8266WiFi.h> // #include <WiFiUDP.h> // #include <Wire.h> // #include <BME280_MOD_1022.h> // #include <Ticker.h> // extern "C" { // #include "user_interface.h" // } // static WiFiUDP UDP; // const char ssid[]="xxxx"; // wifi ssid const char pass[]="xxxx"; // wifi password const char iPhoneIP[] = "192.168.100.22"; // iPhone iP const int iPhoneTxPort = 7000; // UDP Tx port const int iPhoneRxPort = 9000; // UDP Rx port char buff[32]; // rxceive buffer Ticker ticker1; // timer long ta = -6.5; // temp adjust long ha = +10.6; // humidity adjust long pa = +3.5; // press adjust void setup(){ // Serial.begin(115200); // pinMode(D4, OUTPUT);digitalWrite( D4, LOW); // pinMode(D3, OUTPUT);digitalWrite( D3, LOW); // pinMode(D0, OUTPUT);digitalWrite( D0, LOW); // //---------------------------------------------// BME280 setup Wire.begin(D6, D7); // BME280.readCompensationParams(); // BME280.writeOversamplingTemperature(os2x); // temperature x2 BME280.writeOversamplingHumidity(os2x); // humidity x2 BME280.writeOversamplingPressure(os2x); // pressure x2 //---------------------------------------------// WiFi setup WiFi.begin(ssid,pass); // while( WiFi.status() != WL_CONNECTED) { // delay(500); Serial.print("."); // } // Serial.println("WiFi Connect"); // UDP.begin(iPhoneTxPort); // ticker1.attach_ms(6000, report); // } // void loop(){ // ESP.wdtDisable(); // int rx = UDP.parsePacket(); // packet size if (rx){ // recive int len = UDP.read(buff, rx); // rx lengs if (len > 0){ // rx action digitalWrite(D0,HIGH); // ticker1.detach(); // timer off buff[len] = '\0'; // Termination char int c = buff[0]; // header String r = "0"; // No long t; // time if ( c == '1' ){ r = "1"; t = 6*1000;} // 1: 3s: 60 sec if ( c == '2' ){ r = "2"; t = 180*1000;} // 2: 180s: 60 min if ( c == '3' ){ r = "3"; t = 4320*1000;} // 3: 4320s: 24 hr 1day if ( c == '4' ){ r = "4"; t = 30240*1000;} // 4:30240s:168 hr 7day //-----------------------------------------// UDP Transmitt String data = "t/" + r + "/0/0"; // UDP.beginPacket(iPhoneIP, iPhoneRxPort); // UDP port UDP.print(data); // Tx data UDP.endPacket(); // end packet //-----------------------------------------// UDP Transmitt Serial.println(data); // digitalWrite(D0,LOW); // ticker1.attach_ms(t, report); c = '9'; // t timer set & mesure } // } // } // void report(){ // digitalWrite(D3,HIGH); // ESP.wdtDisable(); // //---------------------------------------------// BME280 sensor BME280.writeMode(smForced); delay(50); // chip goes back to sleep digitalWrite(D4,HIGH); // while (BME280.isMeasuring()){delay(250);} // digitalWrite(D4,LOW); // BME280 check BME280.readMeasurements(); // read out the data String t = String(BME280.getTemperature()+ta); // Temp String h = String(BME280.getHumidity() +ha); // Humidity String p = String(BME280.getPressure() +pa); // Pressure String c = String(int(millis()/1000)); // count String data = "d/" + t + "/" + h + "/" + p; // id+temp+html+pres //---------------------------------------------// UDP Transmitt UDP.beginPacket(iPhoneIP, iPhoneRxPort); // UDP port UDP.print(data); // Tx data UDP.endPacket(); // end packet Serial.println(data); // digitalWrite(D3,LOW); // //---------------------------------------------// UDP Transmitt } //
コメントを残す