FlashAirと温度センサーADT7410で温度測定してみた

　知り合いから、とある環境の温度を連続的に測定し、これをクラウドで分析したいのでアドバイスが欲しいとの依頼を受け、秋葉原で温度センサーADT7410を試しに購入してきました。
　ということで、今回は、FlashAirのLuaスクリプト実行環境を使って、秋月電子通商さんのFlashAir用ブレイクアウトボード「FlashAir DIP IOボードキット」で温度センサーADT7410をI2C接続して、温度測定をしてみたいと思います。

#構成
　必要なものは以下のとおりです。

- FlashAir DIP IOボードキット（[秋月電子通商](http://akizukidenshi.com/catalog/g/gK-10007/)）
- FlashAir W-03 V3.00.01アップデート済みのもの
- ADT7410使用高精度・高分解能I2C・16bit温度センサモジュール（[秋月電子通商](http://akizukidenshi.com/catalog/g/gM-06675/)）
- I2C接続小型キャラクタLCDモジュール　8x2行（[秋月電子通商](http://akizukidenshi.com/catalog/g/gP-06669/)）
- ブレッドボード用マイクロＢメスＵＳＢコネクタＤＩＰ化キット（[秋月電子通商](http://akizukidenshi.com/catalog/g/gK-06656/)）
- 3.3V 500mA 三端子レギュレーター（[秋月電子通商](http://akizukidenshi.com/catalog/g/gI-00538/)）
- ブレッドボード

![IMG_0239.jpg](https://qiita-image-store.s3.amazonaws.com/0/89137/b7748d12-165d-bddf-764a-62f69ea3c7a7.jpeg)

#温度センサーADT7410の接続
　ADT7410はアドレス設定ピンA0、A1を２本ともオープン（未接続）のままにして0x48（書き込みアドレス0x90、読み込みアドレス0x91）に設定しています。
　FlashAir DIP IOボードキットとI2C接続の２本の線（SDA、SCL）を結びます。FlashAir DIP IOボードキットの裏面のジャンパーをショートさせてプルアップしています。
　ADT7410は5.0Vでも動作しますが、今回はLCDモジュールの電源供給用の3.3Vで動作させています。

#小型キャラクタLCDモジュールの接続
　ADT7410とともに、LCDモジュールのI2C端子をバス接続しておきます。LCDモジュール単体ではブレッドボードに挿さらないので、[ピッチ変換基盤](http://akizukidenshi.com/catalog/g/gP-06794/)を使うとよいでしょう。
　このLCDモジュールのI2Cアドレスは、0x7C固定です。
　このLCDモジュールは3.3V駆動なので、三端子レギュレータで3.3Vを作って供給しています。

#CONFIGファイルの設定
　FlashAirのSD_WLANフォルダ内のCONFIGファイルに、以下のオプションを追加します。IFMODEはFlashAirのGPIOモードをONにしています。LUA_RUN_SCRIPTはFlashAirの電源投入後すぐに指定のLuaスクリプトを起動するための設定です。
```IFMODE=1
LUA_RUN_SCRIPT=/adt7410_sample.lua
```

　※SD_WLANフォルダとCONFIGファイルは不可視属性ファイルなので注意してください。

#Luaスクリプトファイル
　FlashAirのルートフォルダに以下のコードを記述したテキストファイル（adt7410_sample.lua）をコピーします。
　このコードでは、delaytimeに指定した時間待機して（以下のコードでは10秒）温度測定データをADT7410から受信し、LCDモジュールに表示することを繰り返します。
　待機中の進捗状態がわかるように、LCDモジュールの１行目に表示される"Temp...."の"."（ドット）が１つずつ消えていくようにしています。
　ADT7410はステータス情報を読み出すこともできますが、今回は省略しています。また、ADT7410は４つの測定モード（0:連続変換、1:ワンショット、2: SPS、3:シャットダウン）がありますが、今回は連続変換モードしか試していません。

```adt7410_sample.lua
-- FlashAir DIP IO Board Kit + ADT7410
local spi_init = 1
local spi_mode = 3

--温度センサーADT7410の設定値 ()はデフォルト値
local TempSensorSettingFaultQueue = 0 --(0):1フォルト、1:2フォルト、2:3フォルト、3:4フォルト
local TempSensorSettingCTPin	  = 0 --(0):Active Low 1:Active HIGH
local TempSensorSettingINTPin	  = 0 --(0):Active Low 1:Active HIGH
local TempSensorSettingCIMode	  = 0 --(0):割り込みモード 1:コンパレータモード
local TempSensorSettingMode	  	  = 0 --(0):連続変換、1:ワンショット、2: SPS、3:シャットダウン
local TempSensorSettingADCMode	  = 0 --(0):13ビット、1:16ビット

--温度センサーADT7410のアドレス設定値
local TempSensorWriteAddress      = 0x90
local TempSensorReadAddress       = 0x91

--温度測定周期
local delaytime = 10000 --msec

function delay(t)
	sleep(t)
end

--温度センサーADT7410の初期化
function initTempSensor()
	local settingVal = 0x00
	settingVal = bit32.bor(settingVal, bit32.lshift(TempSensorSettingADCMode, 	 7))
	settingVal = bit32.bor(settingVal, bit32.lshift(TempSensorSettingMode, 		 5))
	settingVal = bit32.bor(settingVal, bit32.lshift(TempSensorSettingCIMode, 	 4))
	settingVal = bit32.bor(settingVal, bit32.lshift(TempSensorSettingINTPin, 	 3))
	settingVal = bit32.bor(settingVal, bit32.lshift(TempSensorSettingCTPin, 	 2))
	settingVal = bit32.bor(settingVal, 				TempSensorSettingFaultQueue	   )
	fa.spi("read")
	fa.spi("cs",0)
	fa.spi("write",0x00)
	fa.spi("write",0x02)
	fa.spi("write",TempSensorWriteAddress)
	fa.spi("write",0x03)
	fa.spi("write",settingVal)
	fa.spi("cs",1)
end

--温度センサーADT7410から温度データを受信する
function getTempSensor()
	local val  = 0x00
	local val1 = 0x00
	local val2 = 0x00
	local tmpval = 0

	fa.spi("read")
	fa.spi("cs",0)
	fa.spi("write",0x01)
	fa.spi("write",0x02)
	fa.spi("write",TempSensorReadAddress)
	fa.spi("cs",1)
	
	fa.spi("read")
	fa.spi("cs",0)
	fa.spi("write",0x06)
	val1 = fa.spi("write", 0xFF)
	val2 = fa.spi("write", 0xFF)
	fa.spi("cs",1)
		
	val = bit32.bor(bit32.lshift(val1, 8), val2)
	if(TempSensorSettingADCMode == 0) then
		--13ビットモードの場合
		val = bit32.rshift(val, 3)
		tmpval = val / 16
		if(val >= 4096) then
			tmpval = (val - 8192) / 16
		end
	else
		--16ビットモードの場合
		tmpval = val / 128
		if(val >= 32768) then
			tmpval = (val - 65536) / 128
		end
	end
	str = string.format("%6.1f", tmpval) .. string.char(0xDF) .. "C"
	return str
end

--I2Cに２バイト分のデータを送信する
function write_i2c_command(addr,data1, data2)
	fa.spi("read")
	fa.spi("cs",0)
	fa.spi("write",0x00)
	fa.spi("write",0x02)
	fa.spi("write",addr)
	fa.spi("write",data1)
	fa.spi("write",data2)
	fa.spi("cs",1)
end

--I2C接続のLCDモジュールを初期化する
function lcd_init()
	write_i2c_command(0x7c, 0x00, 0x38)
	write_i2c_command(0x7c, 0x00, 0x39)
	write_i2c_command(0x7c, 0x00, 0x14)
	delay(10)
	write_i2c_command(0x7c, 0x00, 0x70)
	write_i2c_command(0x7c, 0x00, 0x56)
	write_i2c_command(0x7c, 0x00, 0x6c)
	delay(10)
	write_i2c_command(0x7c, 0x00, 0x38)
	write_i2c_command(0x7c, 0x00, 0x0c)
	write_i2c_command(0x7c, 0x00, 0x01)
	delay(10)
end

--I2C接続のLCDモジュールの画面表示をクリアする
function lcd_clear()
	write_i2c_command(0x7c, 0x00, 0x01)
	delay(5)
end

--I2C接続のLCDモジュールの画面に文字を表示する。LRは0:左揃え、1:右揃えの指定
function lcd_print(value, LR)
	local num = string.len(value)
	if (LR == 1) then
		for idx = 1, 8 - num do
			write_i2c_command(0x7c, 0x40, " ")
		end
	end
	for idx = 1, num do
		write_i2c_command(0x7c, 0x40, string.byte(string.sub(value, idx, idx)))
	end
end

--I2C接続のLCDモジュールの画面に文字を表示する位置を指定する
function lcd_setCursor(x, y)
	write_i2c_command(0x7c, 0x00, bit32.bor(0x80 , y * 0x40 + x))
end

--ここからメイン処理
fa.spi("init", spi_init)
fa.spi("mode", spi_mode)
lcd_init()
delay(100)

initTempSensor()
delay(300)

tmpdelaytime = delaytime / 4
while(1) do
	lcd_setCursor(0x00, 0x00)
	lcd_print("Temp....", 0)
	lcd_setCursor(0x00, 0x01)
	lcd_print(getTempSensor(), 1)
	collectgarbage("collect")
	
	delay(tmpdelaytime)
	for delaycnt = 1, 3 do
		upside_str = "Temp"
		for delaycnt2 = 1, 4 - delaycnt do
			upside_str = upside_str .. "."
		end
		for delaycnt2 = 1, delaycnt do
			upside_str = upside_str .. " "
		end
		lcd_setCursor(0x00, 0x00)
		lcd_print(upside_str, 0)
		sleep(tmpdelaytime)
	end
end
```
[注釈]fa.spi("read")の空readの必要性について
　@Seg_faulさんの解析[「fa.spiの挙動(秋月 FlashAir DIP IOボードキットでfa.spiが動かない問題)」](http://gpsnmeajp.blogspot.jp/2015/12/faspi.html)に書かれている通り、秋月FlashAir DIP IOボードキットでは「CSが反映されないことがあるバグ(or仕様)」により、この空readを入れておかないと期待通りの動作になりません。

#まとめ
　以上で、FlashAirと秋月FlashAir DIP IOボードキットを使って、温度センサーADT7410で温度測定ができるようになりました。今回はそこまで実装していませんが、せっかくのFlashAirですから、取得したデータを無線LANでクラウド環境にアップロードしてもよいですね。Azure IoT HubのREST APIを使ってfa.requestでデータ送信して、Power BIでグラフ化するなどしても良いのではないでしょうか。

#同人誌「宵の空」の通販始まりました
サークル「空と月」による同人誌「宵の空」（2015年12月発行、コミックマーケット89にて頒布）の通販取扱が、秋葉原の三月兎さんで始まりました。コミケに来られなかった方も、ぜひお手に取ってみてください。
[お酒と電子工作について語ってみた本「宵の空」](http://www.march-rabbit.jp/index.php?main_page=product_info&products_id=4155)

![Sora_Lua_表紙s.png](https://qiita-image-store.s3.amazonaws.com/0/89137/a9edca11-5f50-3081-19ff-4bbe8c3f59b0.png)

#参考資料
　[FlashAir DIP IOボードキット](https://flashair-developers.com/ja/documents/resources/dipio/)
　[SC18IS600IBSデータシート](http://cache.nxp.com/documents/data_sheet/SC18IS600.pdf)
　[ADT7410データシート](http://www.analog.com/jp/products/analog-to-digital-converters/integrated-special-purpose-converters/integrated-temperature-sensors/adt7410.html#product-overview)
　[LCDモジュールデータシート](http://akizukidenshi.com/download/ds/xiamen/AQM0802.pdf)