BLE温度計を作ってPythonista3で温度を取得する

#BLE温度計を作ってPythonista3で温度を取得する
##はじめに
そういえば、Pythonista3ってBLE(Bluetooth Low Energy)が扱えるんだなぁ。
そういえば、ESP-WROOM-32のBluetooth機能を使ったことがないなぁ。
じゃあ、BLE温度計から温度を取得してみよう!
##やりたいこと
BLE温度計(ESP-WROOM-32 + 温度センサ)からiPhoneで温度を取得する。

##用意したもの
ESP-WROOM-32(ESP32-DevKitC)
温度センサ DS18B20
iPhone(Pythonista3インストール済み)
##やり方
ESP-WROOM-32とDS18B20を接続し、温度を測定する。
ESP-WROOM-32のBLEで温度を送信する。
温度がどうしても気になるとき、iPhoneで温度を受信する。
###完成イメージ
![system_ds18b20.png](https://qiita-image-store.s3.amazonaws.com/0/140055/2e26807f-329f-5013-eb24-1f4be93ba8cf.png)

##作る
###ESP-WROOM-32とDS18B20を接続する
DS18B20は1-wireで接続する。データシートは次のようになっている。
![ds18B20.png](https://qiita-image-store.s3.amazonaws.com/0/140055/aecb6987-544f-e7e3-6c96-959222c451c3.png)
そのためESP-WROOM-32とは以下のように接続する。
![esp32_ds18B20.png](https://qiita-image-store.s3.amazonaws.com/0/140055/d91e79fa-e131-5068-628b-01b39a165b24.png)
接続完了
![esp32_temp.png](https://qiita-image-store.s3.amazonaws.com/0/140055/67deddb9-93cb-2e41-ea81-aafcdd870088.png)

###温度センサから温度を取得する
DS18B20と1-wire通信を行い、温度を取得する。
プログラムは[feelfreelinux/ds18b20](https://github.com/feelfreelinux/ds18b20)を参考にした。
###ESP-WROOM-32からBLEを使う
####ソースコード
少し長いのでgithubにおいた。
[esp/thermometer](https://github.com/mitazet/esp/tree/master/thermometer/main)
ds18b20.c : 温度センサから値を取得する。
gatts_thermometer.c : ESP32をBLEのサーバーにする。クライアントからのREADイベントでセンサ値を送信する。
※ビルドには[ESP-IDF](https://qiita.com/mitazet/items/8a4c36bd9e1e45f463fd)が必要

プログラムは[ESP-IDF](https://qiita.com/mitazet/items/8a4c36bd9e1e45f463fd)に付属しているGATT Serverサンプルを利用して作成した。
GATTについては[【BLEを使う】GATT（Generic Attribute Profile）概要](http://yegang.hatenablog.com/entry/2014/08/09/195246)がとても参考になった。
製品化するわけではないので、UUIDは適当な値を使用している。
###Pythonista3からBLEを使う
####ソースコード
Pythonista3で[cb](http://omz-software.com/pythonista/docs/ios/cb.html)を使用し、温度データを取得する。
UUIDは上記のプログラムに合わせている。

```py3:thermo_client.py
import ui
import cb
import sound
import struct

TM_SERVICE_UUID = '00FF'
TM_CHAR_UUID = 'FF01'

class MyCentralManagerDelegate (object):
	def __init__(self):
		self.peripheral = None
		self.temp = 0

	def did_discover_peripheral(self, p):
		global text_state
		print('+++ Discovered peripheral: %s (%s)' % (p.name, p.uuid))
		if p.name and 'ESP_THERMOMETER' in p.name and not self.peripheral:
			# Keep a reference to the peripheral, so it doesn't get garbage-collected:
			self.peripheral = p
			cb.connect_peripheral(self.peripheral)
			text_state.text = 'Detected'

	def did_connect_peripheral(self, p):
		print('*** Connected: %s' % p.name)
		print('Discovering services...')
		p.discover_services()

	def did_fail_to_connect_peripheral(self, p, error):
		print('Failed to connect')

	def did_disconnect_peripheral(self, p, error):
		print('Disconnected, error: %s' % (error,))
		self.peripheral = None

	def did_discover_services(self, p, error):
		for s in p.services:
			if TM_SERVICE_UUID in s.uuid:
				print('+++ Thermometer found')
				p.discover_characteristics(s)

	def did_discover_characteristics(self, s, error):
		if TM_SERVICE_UUID in s.uuid:
			for c in s.characteristics:
				if TM_CHAR_UUID in c.uuid:
					print('read temperature sensor...')
					self.peripheral.read_characteristic_value(c)

	def did_write_value(self, c, error):
		print('Did enable temperature sensor')

	def did_update_value(self, c, error):
		global text_temp
		if TM_CHAR_UUID == c.uuid:
			# 
			self.temp = (c.value[0] + (c.value[1]*256))/16
			print(self.temp)
			text_temp.text=(str(self.temp) + '℃')

view = ui.View()                                      
view.name = 'THERMOMETER'                                    
view.background_color = 'white'

text_state = ui.TextView()
text_state.frame = (view.width * 0.5, view.height * 0.2, view.width, view.height*0.3)
text_state.flex = 'LRTB'
text_state.font = ('<system>', 18)
text_state.text_color = 'grey'
 
text_temp = ui.TextView()
text_temp.frame = (view.width * 0.25, view.height * 0.4, view.width, view.height)
text_temp.flex = 'WHLRTB'
text_temp.font = ('<system-bold>', 50)                                                    

view.add_subview(text_state)
view.add_subview(text_temp)
view.present('sheet')

delegate = MyCentralManagerDelegate()
print('Scanning for peripherals...')
text_state.text = 'Scanning'
cb.set_central_delegate(delegate)
cb.scan_for_peripherals()

# Keep the connection alive until the 'Stop' button is pressed:
try:
	while True: pass
except KeyboardInterrupt:
	# Disconnect everything:
	cb.reset()
```
こちらもgithubにおいた。[thermo_client.py](https://github.com/mitazet/python_code/blob/master/thermo_client.py)

##テスト
Pythonista3でtermo_client.pyを実行。
<img width="375" alt="5C5AAD7B-08A1-43DC-93CC-39F265A8BEF1.jpeg" src="https://qiita-image-store.s3.amazonaws.com/0/140055/0134760c-3c60-1c22-ec35-456e5d34c0b6.jpeg">
取得成功!!
少し肌寒い温度。
##最後に
__リモートにする意味あるのか__というツッコミは受け付けません。
家の中ではあまり役に立たないが、農場とかでは役に立つかもしれない(省電力だし)。
##参考サイト
[ubuntuからESP32にbluetooth接続して信号を送受信する方法](http://asukiaaa.blogspot.jp/2016/12/ubuntuesp32bluetooth.html)
[cb — Connecting to Bluetooth LE Peripherals](http://omz-software.com/pythonista/docs/ios/cb.html)
[feelfreelinux/ds18b20](https://github.com/feelfreelinux/ds18b20)
[【BLEを使う】GATT（Generic Attribute Profile）概要](http://yegang.hatenablog.com/entry/2014/08/09/195246)