Rasberry pi 3でストロベリー・リナックス社製の「TSL2561 照度センサ・モジュール （メーカー品番：TSL2561）」を使う（まとめ編）

必要なところだけまとめました。試行錯誤編は[こちら](http://qiita.com/boyaki_machine/items/a238e9d03455a2eea26e)です。

2017-04-09 githubにupしてました。
https://github.com/boyaki-machine/TSL2561

# 物理配線
届いたセンサに、ピンヘッダをハンダ付けします。その後、配線します。

> センサ1pin(GND) -> Raspi 6番pin
> センサ2pin(SDA) -> Raspi 3番pin
> センサ3pin(SCL) -> Raspi 5番pin
> センサ4pin(VDD) -> Raspi 1番pin

こんな感じです。
![TSL2561.png](https://qiita-image-store.s3.amazonaws.com/0/123906/f61fb727-14ba-f689-088a-f914a5c762ce.png)

# Raspberry piでI2Cを使えるようにする

OSのコンフィグメニューで設定。

```zsh
$ sudo raspi-config
```
"9 Advenced Options” -> “A7 I2C” の順にメニューを選択。
「Would you like the ARM I2C interface to be enabled?」と聞いてくるのでyesを選択。
「Would you like the I2C kernel module to be loaded by default?」と聞いてくるのでyesを選択。

次に、/boot/config.txtを編集。

```zsh
$ sudo vi /boot/config.txt
...以下の内容を追記
dtparam=i2c_arm=on
```
更に、/etc/modulesを編集。

```zsh
$ sudo vi /etc/modules
...以下の内容を追記
snd-bcm2835
i2c-dev
```

設定終了後、Raspyを再起動。
再起動後にカーネルモジュールがロードされていることを確認。

```zsh
$ lsmod
...
i2c_dev                 6709  0 
snd_bcm2835            21342  0 
...
```
# ツール類をインストールする
```zsh
$ sudo apt-get install i2c-tools python-smbus
```

# アドレス確認
センサのアドレスを確認します。

```zsh
$ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- 39 -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
```
アドレス0x39で認識。

# Pythonでデータを取得する
以下のクラスを使います。

```py
#!/usr/bin/python -u
# -*- coding: utf-8 -*-

import smbus
import time

# Strawberry Linux社の「TSL2561 照度センサ・モジュール」から
# I2Cでデータを取得するクラス
# https://strawberry-linux.com/catalog/items?code=12561
# 2016-05-03 Boyaki Machine
class SL_TSL2561:
	def __init__(self, address, channel):
		self.address	= address
		self.channel	= channel
		self.bus		= smbus.SMBus(self.channel)
		self.gain 		= 0x00			# 0x00=normal, 0x10=×16
		self.integrationTime 	= 0x02	# 0x02=402ms, 0x01=101ms, 0x00=13.7ms
		self.scale 		= 1.0

		# センサ設定の初期化
		self.setLowGain()
		self.setIntegrationTime('default')

	def powerOn(self):
		self.bus.write_i2c_block_data(self.address, 0x80, [0x03])
		time.sleep(0.5)

	def powerOff(self):
		self.bus.write_i2c_block_data(self.address, 0x80, [0x00])

	# High Gainにセットする(16倍の感度？)
	def setHighGain(self):
		# High Gainにするとうまくrawデータが取れないことがある。
		# 要原因調査 ( 5047固定値になる )	
		self.gain 	= 0x10
		data 		= self.integrationTime | self.gain
		self.bus.write_i2c_block_data(self.address, 0x81, [data])
		self.calcScale()

	# Low Gain(default) にセットする 
	def setLowGain(self):
		self.gain 	= 0x00
		data 		= self.integrationTime | self.gain
		self.bus.write_i2c_block_data(self.address, 0x81, [data])
		self.calcScale()

	# 積分する時間の設定（１回のセンシングにかける時間？）
	# val = shor, middle, logn(default)
	def setIntegrationTime(self, val):
		if val=='short':
			self.integrationTime 	= 0x00	# 13.7ms scale=0.034
		elif val=='middle':
			self.integrationTime 	= 0x01 	# 101ms	 scale=0.252
		else:
			self.integrationTime 	= 0x02  # defaultVal 402ms  scale=1.0
		data = self.integrationTime | self.gain
		self.bus.write_i2c_block_data(self.address, 0x81, [data])
		self.calcScale()

	def getVisibleLightRawData(self):
		data 	= self.bus.read_i2c_block_data(self.address, 0xAC ,2)
		raw 	= data[1] << 8 | data[0]	# 16bitで下位バイトが先
		return raw

	def getInfraredRawData(self):
		data 	= self.bus.read_i2c_block_data(self.address, 0xAE ,2)
		raw 	= data[1] << 8 | data[0]	# 16bitで下位バイトが先
		return raw

	def getRawData(self):
		data 	= self.bus.read_i2c_block_data(self.address, 0xAC ,4)
		VL 		= data[1] << 8 | data[0]	# 可視光　16bitで下位バイトが先
		IR 		= data[3] << 8 | data[2]	# 赤外線　16bitで下位バイトが先
		return (VL,IR)

	def calcScale(self):
		_scale = 1.0
		# integrationTimeによるスケール
		if self.integrationTime == 0x01:	# middle
			_scale = _scale / 0.252
		elif self.integrationTime == 0x00:	# short
			_scale = _scale / 0.034
		
		# gainによるスケール
		if self.gain == 0x00 :				# gain 1
			_scale = _scale * 16.0

		self.scale = _scale

	def getLux(self):
		# センサ生データの取得
		raw  = self.getRawData()

		# 65535の時はエラー出力にする実装
		if raw[0] == 65535 or raw[1] == 65535:
			return "Range Over"

		# センサ設定により生データをスケールする
		VLRD = raw[0] * self.scale
		IRRD = raw[1] * self.scale

		# 0の除算にならないように               
		if (float(VLRD) == 0):
			ratio = 9999
		else:
			ratio = (IRRD / float(VLRD))

		# Luxの算出
		if ((ratio >= 0) & (ratio <= 0.52)):
			lux = (0.0315 * VLRD) - (0.0593 * VLRD * (ratio**1.4))
		elif (ratio <= 0.65):
			lux = (0.0229 * VLRD) - (0.0291 * IRRD)
		elif (ratio <= 0.80):
			lux = (0.0157 * VLRD) - (0.018 * IRRD)
		elif (ratio <= 1.3):
			lux = (0.00338 * VLRD) - (0.0026 * IRRD)
		elif (ratio > 1.3):
			lux = 0

		return lux 


if __name__ == "__main__":
	sensor 	= SL_TSL2561(0x39,1) 
	sensor.powerOn()
	# sensor.setHighGain()
	sensor.setIntegrationTime('default')
	while True:
		print "Lux : " + str(sensor.getLux())
		time.sleep(1.0)

```

# 留意点

- High Gainにした時、ある程度の照度（５月日中の窓際くらい）があると異常な値に固定される問題があります。