I2C接続のLED マトリックスにパターン表示

リモートセンサー用CPU ボードの使用例として、I2C バスで接続できる Adafruit社製の 8×8 LED マトリックスを接続してみたいと思います。

オールブルーシステムの DeviceServer から API 経由でリモートCPUボードの I2C バスを操作すると簡単なのですが、あえて最初は DeviceServer を使用せずに XBee-ZB のフレームデータを直接 X-CTU から送信してリモートCPU ボードを操作してみます。

CPU ボードと LED マトリックスを接続した様子です。

CPU ボードの手前 20ピンには DIO, A/D, SPI, I2C の各I/O信号と 5V, 3.3V, GND の電源ラインが接続できるようになっています。今回は 5V, GND 電源ラインと I2C の SCL, SDA の4本のみを接続します。CPU ボードについてはこちらのリンクから回路図や組み立て方を紹介しています。

CPU ボードで動作させるプログラムは TDCPZB for ATmega328 で、XBee-ZBのAPI フレームに格納した リモートコマンドを使用して操作することができます。リモートコマンドはascii 文字列で構成されていて、このリモートコマンド文字列を XBee-ZB の API Type 0×10 で送信するRF データに格納して送信します。CPU ボードに接続した XBee-ZB でRFデータを受信すると、CPU がRDデータ中のコマンド文字列を実行して、実行結果の文字列を リモート側のXBee-ZB デバイスから同様にフレームデータに格納して、リモートコマンド送信元に返します。今回は I2C バスを操作するリモートコマンド “i2c_write” だけを使用します。

CPU ボードに搭載している End Device のXBee-ZB とは別に、PC に接続した XBee-ZB が Coordinator として動作しています。ここで、Digi International から提供されている X-CTU プログラムを使って リモートコマンドを格納したAPI フレームを送信してみます。

最初にCoordinator が接続されている PC で X-CTU プログラムを起動します。

プログラムが起動してメイン画面が表示されたら、”Add devices” のボタンを押します。

Coordinator XBee-ZB を PC に接続している XBee Explorer 等で作成されたCOM ポートを選択します。

Coordinator XBee-ZB デバイスが表示されます。ここで “discover radio nodes ….” と表示されるネットワーク状のアイコンを選択してPAN 内の Router と End device を探します。

デバイスが見つかったら、”Add selected devices” を押してデバイス一覧を表示させます。

リモートセンサー用のCPU ボードに搭載した XBee-ZB は、”Node1″ の End Device として表示されています。

ここで、リモートセンサー用CPU ボードで実行している TDCP for ATmega328 プログラムのリモートコマンドを送信するために、API フレームデータを作成します。最初に X-CTU と Coordinator デバイスを console モードで接続します。”>” のマークが描かれたタブを選択して、コネクタ状のイラストのかかれたボタンをクリックします。

Coordinator デバイスとコンソール接続されたら、送信先となるリモート側の “Node1″ デバイスをマウスで右クリックして 64bitアドレス(MAC address)をクリップボードにコピーしておきます。

“Send frames” と書かれた部分にある “+” を押すと表示されるダイアログ中の”frame generator” ツールを起動します。

最初にFrame type を 0×10 に変更します。ここで先ほどクリップボードにコピーした送信先となる “Node1″ の 64ビットアドレスを “64bit dest address … “の入力欄にペーストします。次に、”RF Data” の入力欄の “ASCII” タブを選択して、ここにリモートCPU ボードに指示するリモートコマンドを入れます。

“$$$,i2c_write,70,0000…….. ” の部分がリモートコマンドです。$$$ を指定すると、CPU ボードが “$$$,” 以降の部分をコマンドとして実行します。この i2c_write コマンドでは 0×70 のi2c slave デバイスに対して、0×00,0×00 …… のバイト列を書き込んでいきます。今回はリモートコマンドの実行結果は必要ありませんのでコマンド実行結果のリプライ送信の指示は行っていません。i2c センサーデバイスなどからデータを取得する場合には $$$ の代わりに $$$abc の様に任意の英数字(5文字まで)を $$$ の後ろに付けます。このようにすると、リモートCPU ボードからは同じく API type 0×10 で実行結果を格納したフレームデータがコマンド送信元の XBee-ZB に対して送信されてきます。

CPU ボードで実行可能なリモートコマンドの詳細は TDCPZB for ATmega328 ユーザーマニュアルをご覧ください。

フレームデータの作成が終了したら、”frame generator” の “OK” を押します。

“Send selected frame” ボタンを押して、先ほど作成したフレームデータを Coordinator に送信します。これは “Node1″ に対する RFデータ送信(API Type 0×10) を Coordinator に依頼しています。送信すると、Coordinator からリプライフレーム”transmit status” が返されてきます。

“frame details” 欄の “delivery status” が 0×00 “Success” になっていればCPU ボードに RF Data (リモートコマンド文字列が格納されていた) の送信が完了していることになります。

CPU ボードと LED マトリックスが正常に動作していると、以下のように表示されます。

LED マトリックスに表示するためには、下記の初期設定コマンドも一度リモートCPU ボードで実行しておく必要があります。

i2c_write,70,21 — turn on oscillator
i2c_write,70,81 — blink off and display on
i2c_write,70,E3 — brightness max

また、LED マトリックスのビットパターンはそのままの点灯パターンを送信するのではなく、ビット位置の MSB <-> LSB の入れ替えと、 バイト単位で1 ビット右ローテーション、ワード(16bit) への拡張(0×00 をパディング) を施したデータを I2C データとして書き込んでいます。

通常はCoordinator からはプログラム経由で API フレームを作成しますが、今回はあえて手動で実行させてみました。DeviceServer を使用すると下記のように簡単に Web API経由でWeb ブラウザ中のJavaScript やクラウド環境、スマートフォンなどから直接ビットパターンを指定して表示させることができます。

X-CTU で送信したときと同じビットパターンの絵を LED マトリックスに表示させるには、Webブラウザから URL を指定してアクセス(HTTP GET)します。

data パラメータには、8×8 マトリックスのビットパターンを16進数で指定しています。

0×00 -> 0b00000000
0×10 -> 0b00010000
0×60 -> 0b01100000
0xFE -> 0b11111110
0x0F -> 0b00001111
0x0F -> 0b00001111
0×06 -> 0b00000110
0×00 -> 0b00000000

(’1′ 部分が音符の絵のビットパターンに対応しています)

このビットパターンデータから、リモートCPU ボードで実行する “i2c_write” コマンド文字列にサーバー側の Lua スクリプトで変換しています。session パラメータにはDeviceServer に予め認証を省略するために作成したセッショントークンを指定しています。nameパラメータで指定している Lua スクリプト”ZB/DEVICE/ADA_LED8X8_DISP” の内容は下記になります。

file_id = "ADA_LED8X8_DISP"

--[[

Adafruit Mini 8x8 LED Matrix w/I2C Backpackにピクセルデータを表示する

スクリプト起動時に渡されるパラメータ
---------------------------------------------------------------------------------
キー値			値		            						値の例
---------------------------------------------------------------------------------
device		XBee-ZB デバイス64ビットアドレスまたは NodeIdentifier	"Node1"

data		LED 8x8マトリックスに表示する点灯パターン
			8 バイト分のデータを16進数文字列で連結したものを指定する
			MSB は LED マトリックスの ROW#0 側で LSB が ROW#7 に対応する。

			全ピクセル点灯									"FFFFFFFFFFFFFFFF"
			全ピクセル消灯									"0000000000000000"
			四角枠のピクセルを点灯							"FF818181818181FF"

]]

-------------------------
-- パラメータチェック
-------------------------
if not (g_params["device"] and g_params["data"]) then
	log_msg("parameter error",file_id)
	error()
end

local xbee = g_params["device"]
local slave_addr = "70"

---------------------------------------------------------------------------------
-- LED マトリックスデバイスの初期化
---------------------------------------------------------------------------------
function init_device()
	-----------------------------
	-- turn on oscillator
	-----------------------------
	if not zb_tdcp_safe_retry(xbee,"i2c_write," .. slave_addr .. ",21") then error() end

	-----------------------------
	-- blink off and display on
	-----------------------------
	if not zb_tdcp_safe_retry(xbee,"i2c_write," .. slave_addr .. ",81") then error() end

	-----------------------------
	--  brightness
	-----------------------------
--	if not zb_tdcp_safe_retry(xbee,"i2c_write," .. slave_addr .. ",EF") then error() end -- max
	if not zb_tdcp_safe_retry(xbee,"i2c_write," .. slave_addr .. ",E3") then error() end -- 1/4

	log_msg("device initialized",file_id)
end

---------------------------------------------------------
-- カラムデータバイトの低位ビットと高位ビットを入れ替える
---------------------------------------------------------
function swap_bits(x)
	local ret = 0
	if (bit_and(x,0x01) ~= 0) then ret = ret + 0x80 end
	if (bit_and(x,0x02) ~= 0) then ret = ret + 0x40 end
	if (bit_and(x,0x04) ~= 0) then ret = ret + 0x20 end
	if (bit_and(x,0x08) ~= 0) then ret = ret + 0x10 end
	if (bit_and(x,0x10) ~= 0) then ret = ret + 0x08 end
	if (bit_and(x,0x20) ~= 0) then ret = ret + 0x04 end
	if (bit_and(x,0x40) ~= 0) then ret = ret + 0x02 end
	if (bit_and(x,0x80) ~= 0) then ret = ret + 0x01 end
	return ret
end

--------------------------------------------------
-- カラムデータバイトを 1 ビット右ローテートさせる
--------------------------------------------------
function wrap_pixel(x)
	local ret = bit_rshift(x,1)
	if (bit_and(x,0x01) ~= 0) then
		ret = ret + 0x80
	end
	return ret
end

---------------------------------------------
-- 入力データを LED マトリックス用に変換する
---------------------------------------------
local data16 = ""
local arr = hex_to_tbl(g_params["data"])
for key,val in ipairs(arr) do
	data16 = data16 .. bit_tohex(wrap_pixel(swap_bits(val)),2) .. "00"
end

---------------------------------------------------------------
-- このスクリプトが最初に呼ばれたときにはデバイスを初期設定する
---------------------------------------------------------------
local stat,val = get_shared_data("Adafruit_LED8X8_" .. g_params["device"])
if not stat then error() end
if (val == "") then
	init_device()
	if not set_shared_data("Adafruit_LED8X8_" .. g_params["device"],"1") then error() end
end

---------------------------------------------------------------
-- レジスタ 0x00 からピクセルデータを順に書き込む
---------------------------------------------------------------
if not zb_tdcp_safe_retry(xbee,"i2c_write," .. slave_addr .. ",00" .. data16) then error() end
-- log_msg(data16,file_id)

X-CTU を使って API フレームデータを送信したときに手動で計算した、ビットパターンから I2C データへの変換もスクリプト内部で実行しています。また、LED デバイスの初期化も自動的に判断して実行しています。