Raspberry PiとBME280でXivelyにデータを送信するガジェットをつくる

最近BME280という湿度、気圧、温度複合センサモジュールが電子部品店で販売されるようになりました。

http://akizukidenshi.com/catalog/g/gK-09421/

  • スイッチサイエンス

https://www.switch-science.com/catalog/2236/

https://strawberry-linux.com/catalog/items?code=12280/


これ1つで湿度、気圧、温度をI2C/SPIで取得することができるので便利そうです。
今回はこのセンサーの値をRaspberry Pi A+上のPythonで取得し、IoT向けサービスであるXivelyにデータを送信するところまで行いました。

使用したハードウェア、ソフトウェア

  1. Raspberry Pi A+
  2. WiFiドングル(LAN-W150N/U2)
  3. BME280使用 温湿度・気圧センサモジュールキット: センサ一般 秋月電子通商 電子部品 ネット通販
  4. Raspbian 2015-05-15
  5. Python 2.7.3

BME280とRaspberry Piの接続

図のように接続します。
I2Cを使用し、オンボードの抵抗を使うのでJ1、J2、J3は全てショートしてください。
f:id:penta_twi:20150701230442p:plain

I2Cの有効化

RaspbianのデフォルトではI2Cは無効化されているのでこれを有効化します。
次の記事を参考にI2Cを有効化しました。
Raspberry Pi の I2C を有効化する方法 (2015年版) - 意識低い開発者のBlog
記事の通りに設定を行ったら

sudo apt-get i2c-tools

を入力します。

各種Pythonライブラリのインストール

smbusモジュールのインストール
PythonでI2Cを使用するためにはsmbusモジュールが必要です。
次のコマンドでインストールします。

$ sudo apt-get install python-smbus

requestsモジュールのインストール

Xivelyにデータを送信する部分はこの記事を参考に書きました。yamaryu0508.hatenablog.com
この記事ではHTTPリクエストにPythonのrequestsモジュールを使用しているのでこちらをインストールします。
また、最新のrequestsモジュールを使用するとInsecurePlatformWarningが出るのでrequests2.5.3を使用しました。

sudo apt-get install python-pip
sudo pip install requests==2.5.3

Xivelyへ登録、チャンネルの作成

Xivelyは個人向けサービスと法人向けサービスに分けられ、個人向けサービスはPersonal Xivelyへと変更されたようです。
登録はここから行います。
Sign Up - Xively

登録の方法とチャンネルの作成などは次の記事を参考に行いました。
この夏,チャレンジ!中学生でも開発可能なM2Mシステム Part3 ―― 3G通信モジュールとクラウド・システムの連携[応用編]|Tech Village (テックビレッジ) / CQ出版株式会社

デバイスRapberry Piに対してチャンネル

  • Temperature
  • Humidity
  • Pressure-Altitude_600m
  • Pressure-Aititude_0m

を作成しました。

Pythonスクリプトの作成

いよいよPythonスクリプトを作成します。
BME280は生のデータから計算して湿度と気圧を算出する必要があります。
データシートや解説を読んでもなんのこっちゃという感じなのでmbedのライブラリのコードを拝借しPython用に書き換えました。雪だるま大先生に感謝です。
BME280 - a mercurial repository | mbed

APY_KEYとFEED_IDは自分のXivelyのチャンネルのものに書き換えてください。
ALTITUDEは実験している場所の高度をメートルで書き換えてください
registToXivelyの第1引数はチャンネル名を指定するのでここも適宜書き換えてください。
今回初めてPythonを使ったのでご了承ください。直した方がいい点などあればご指摘お願いします。

大気圧がPressure-Altitude_0mとPressure-Altitude_600mに分けられている理由は気象庁のデータは海面更正というものを行っていて取得地点の高度のデータを0mの高度のデータに換算しているため、気象庁のデータと比較したい場合にはこの海面更正をする必要があります。
詳しくは気象観測の手引き(pp.37-39)をお読みください。

#!/usr/bin/python

import sys,time
import smbus
import urllib2, json, requests

INTERVAL = 60*0.5 # second

API_KEY = "YOUR API KEY"
FEED_ID = "YOUR FEED ID"

ALTITUDE= 600

class Xively:
	def __init__(self,feedId,apiKey):
		self.feedId = feedId
		self.apiKey = apiKey
	def registToXively(self,channel,dataPoints):
		request = { 'datastreams' : [ {'id' : channel, 'current_value' : dataPoints}]}
		requestJson = json.dumps(request)
		url = "https://api.xively.com/v2/feeds/" + self.feedId + ".json"
		headers = {"X-ApiKey": self.apiKey}
		res = requests.put(url, headers=headers, data=requestJson)
		return res

xivelyDevice = Xively(FEED_ID,API_KEY)

class BME280:
	DEVICE_ADDRESS = 0x76
	BUS_CHANNEL = 1

	def __init__(self,address = DEVICE_ADDRESS,channel = BUS_CHANNEL):
		self.address = address
		self.channel = channel

		self.bus = smbus.SMBus(self.channel)
		self.t_fine = 0

		data = self.bus.read_byte_data(self.address,0xD0)
		# print 'ID:0x%x' % data

		#cfrl hum Humidity oversampling x1
		self.bus.write_byte_data(self.address,0xF2,0x01)

		#ctrl meas  Temparature oversampling x1, Pressure oversampling x1, Normal mode
		self.bus.write_byte_data(self.address,0xF4,0x27)

		#config Standby 1000ms ,Filter off
		self.bus.write_byte_data(self.address,0xF5,0xA0)

		data = self.bus.read_i2c_block_data(self.address,0x88,6)

		self.dig_T1 = (data[1] << 8) | data[0]
		self.dig_T2 = (data[3] << 8) | data[2]
		self.dig_T3 = (data[5] << 8) | data[4]

		data = self.bus.read_i2c_block_data(self.address,0x8E,18)

		self.dig_P1 = (data[ 1] << 8) | data[ 0]
		self.dig_P2 = (data[ 3] << 8) | data[ 2]
		self.dig_P3 = (data[ 5] << 8) | data[ 4]
		self.dig_P4 = (data[ 7] << 8) | data[ 6]
		self.dig_P5 = (data[ 9] << 8) | data[ 8]
		self.dig_P6 = (data[11] << 8) | data[10]
		self.dig_P7 = (data[13] << 8) | data[12]
		self.dig_P8 = (data[15] << 8) | data[14]
		self.dig_P9 = (data[17] << 8) | data[16]


		data[0] = self.bus.read_byte_data(self.address,0xA1) #read dig_H regs

		self.dig_H1 = data[0]

		data = self.bus.read_i2c_block_data(self.address,0xE1,7)

		self.dig_H2 = (data[1] << 8) | data[0]
		self.dig_H3 = data[2]
		self.dig_H4 = (data[3] << 4) | (data[4] & 0x0f)
		self.dig_H5 = (data[5] << 4) | ((data[4]>>4) & 0x0f)
		self.dig_H6 = data[6]


	def getTemperature(self):
		temp_xlsb = self.bus.read_byte_data(self.address,0xFC)
		# print '0x%x' % temp_xlsb

		temp_lsb = self.bus.read_byte_data(self.address,0xFB)
		# print '0x%x' % temp_lsb

		temp_msb = self.bus.read_byte_data(self.address,0xFA)
		# print '0x%x' % temp_msb

		temp_raw = (temp_msb << 12) | (temp_lsb << 4) | (temp_xlsb >> 4)

		temp_data = (((((temp_raw >> 3) - (self.dig_T1 << 1))) * self.dig_T2) >> 11) +\
			 ((((((temp_raw >> 4) - self.dig_T1) * ((temp_raw >> 4) - self.dig_T1)) >> 12) * self.dig_T3) >> 14)

		self.t_fine = temp_data
		temp_data = (temp_data * 5 + 128) >> 8
		return temp_data / 100.0


	def getPressure(self):
		data = self.bus.read_i2c_block_data(self.address,0xF7,3)
		press_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)

		var1 = (self.t_fine >> 1) - 64000
		var2 = (((var1 >> 2) * (var1 >> 2)) >> 11) * self.dig_P6
		var2 = var2 + ((var1 * self.dig_P5) << 1)
		var2 = (var2 >> 2) + (self.dig_P4 << 16)
		var1 = (((self.dig_P3 * (((var1 >> 2)*(var1 >> 2)) >> 13)) >> 3) + ((self.dig_P2 * var1) >> 1)) >> 18
		var1 = ((32768 + var1) * self.dig_P1) >> 15
		if var1 == 0:
			return 0

		press = (((1048576 - press_raw) - (var2 >> 12))) * 3125
		if press < 0x80000000:
			press = (press << 1) / var1
		else :
			press = (press / var1) * 2

		var1 = (self.dig_P9 * ((((press >> 3) * (press >> 3)) >> 13))) >> 12
		var2 = (((press >> 2)) * self.dig_P8) >> 13
		press = (press + ((var1 + var2 + self.dig_P7) >> 4))

		return (press/100.0)

	def getHumidity(self):
		data = self.bus.read_i2c_block_data(self.address,0xFD,2)

		hum_raw = (data[0] << 8) | data[1]

		v_x1 = self.t_fine - 76800
		v_x1 =  (((((hum_raw << 14) -((self.dig_H4) << 20) - ((self.dig_H5) * v_x1)) +\
			(16384)) >> 15) * (((((((v_x1 * self.dig_H6) >> 10) *\
			(((v_x1 * (self.dig_H3)) >> 11) + 32768)) >> 10) + 2097152) *\
			self.dig_H2 + 8192) >> 14))
		v_x1 = (v_x1 - (((((v_x1 >> 15) * (v_x1 >> 15)) >> 7) * self.dig_H1) >> 4))
		if v_x1 < 0:
			v_x1 = 0
		if v_x1 > 419430400:
			v_x1 = 419430400
		
		hum = (v_x1 >> 12)
		
		return (hum/1024.0)


bme280 = BME280()
log = open("log.csv","a")
while True:
	temp = round(bme280.getTemperature(),1)
	hum = round(bme280.getHumidity(),1)
	press = round(bme280.getPressure(),1)

	pressCorrect = round((press  * 9.81 * ALTITUDE) / (287 * (273.15 + temp)),1) 
	print_msg =  "Temp,%0.1f,Hum,%0.1f,Press,%0.1f,0m_Press,%0.1f" %(temp,hum,press,press+pressCorrect)
	print print_msg
	log.write(print_msg+"\n")
	xivelyDevice.registToXively('Temperature',temp)
	xivelyDevice.registToXively('Humidity',hum)
	xivelyDevice.registToXively('Pressure-Altitude_600m',press)
	xivelyDevice.registToXively('Pressure-Altitude_0m',press + pressCorrect)
	time.sleep(INTERVAL)

実行と確認

作成したPythonスクリプトを次のコマンドで実行します。

sudo python raspi_xively.py

そして自分のXivelyのURLへ行き、正しくデータが送信されていることを確認します。
f:id:penta_twi:20150701130958p:plain
これであなたの情報が公開されるようになりました、やったね。(Xivelyにはプライベートモードもあるので情報を晒すのが嫌だ、仲間内だけで使いたい場合はそちらをご利用ください。)