macsbug

Display the graph on the iPad

leave a comment »

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
}                                                //

Written by macsbug

8月 8, 2016 @ 11:48 am

カテゴリー: ESP8266, Pythonista

コメントを残す