3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

AIスピーカーとFlashAirで鉄道模型を動かしてみた

Last updated at Posted at 2018-03-03

AIスピーカーが発売に

 2017年秋ごろから、日本でも各社からAIスピーカーが発売になりました。たまたまGoogle Homeを入手したので、これを使って鉄道模型をコントロールできたら面白いなと思い、構成を考えはじめました。もともとiOSアプリで音声認識を使って鉄道模型をコントロールすることは実現できていたのですが、スマフォよりもAIスピーカーのほうがより自然にコントロールできるような気がしたのでした。
 そこで、今回はAIスピーカー(Google Home)とFlashAirを連携させて、鉄道模型をコントロールしてみます。

実現すること

 今回作るものが動作している様子を最初にご覧ください。最終的にこれを実現します。
 YouTube:Google HomeとFlashAirで鉄道模型をコントロールしてみた

全体構成

 今回作るものの全体構成を下図に示します。
image1.png
image2.png

コンセプト

今回作るもののコンセプトはこのように設定しました。

  • できるだけコーディングレスで構築すること
  • できるだけMicrosoft AzureのPaaSを用いること
  • さまざまなAIスピーカーに対応すること

構成要素

 今回作るものの構成要素は、以下の通りです。

IFTTT

 最初に、AIスピーカーからWebリクエストを発行するアプレットを新規に作成します。
 IFTTTのアカウントを持っていない場合は、アカウントを作成してください。その後、新しいアプレットを作成します。
 「IF this THEN that」において、「this」に「Google Assistant」の「Say a phrase with a text ingredient」を選択します。また「that」には「Web Hooks」の「Make a web request」を選択します。
 各設定項目は、下図のように設定します。
 なお、WebHooksのURLは、後ほど作成するLogic AppsのオリジナルWebAPIのURLを設定しますので、この時点では空白で良いです。
特定ワードは、「フラッシュエアー」としました。

image3.png

LUIS

 LUISによる鉄道模型コントロール用文章認識のアプリケーションを新規に作成しておきます。
 LUISアプリケーションの作成方法は、こちらのサイト「LUIS 入門(Cognitive Services - 2017年12月版 - 1/2)」が参考になります。

Azure Logic Apps

 Logic AppsによるオリジナルWebAPIを作成します。こちらの記事を参照してください。

Azure Functions

 Logic AppsからAzure IoT HubのC2Dメッセージの送信を行うコネクタが標準で提供されていないため、Azure Functionでメッセージ送信用WebAPIを作成します。作成したメッセージ送信用WebAPIは、先のLogic Appsから呼び出します。こちらの記事を参照してください。

Azure IoT Hub

 Azure IoT Hubによるデバイス管理を行います。Azure IoT Hubを新規に作成し、FlashAirをデバイスとして登録します。
 ここでAzure IoT Hubを作成した時の名称と「設定」-「共有アクセスポリシー」にある共有アクセスキー(主キー)を取得しておきます。
 Azure IoT Hubの作成については、以下のチュートリアルを参考にしてください。

FlashAir

 FlashAirでは、LuaスクリプトによりI2C接続のモータドライバーをコントロールできるようにします。その上で、さらにAzure IoT HubのC2Dメッセージを定期的に取得するようにコーディングします。

CONFIGファイルの設定

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

IFMODE=1
LUA_RUN_SCRIPT=/ai_speaker_sample.lua

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

Luaスクリプトファイル

 FlashAirをAzure IoT Hubに接続するにあたって、cocteau666さんがGitHubのFlashAir_AzureIoTで公開されている以下のソースコードを利用させていただきます。

 また、Timestampを取得するため、同じくcocteau666さんがGitHubのFlashAir_Timestampで公開されている以下のソースコードを利用させていただきます。

 ただし、今回使うFaTimestamp.luaは、コピーしたあとに9行目以降のコードを削除してください。saveTime関数で使われているos関数はFlashAirでは使えないためです。
 この3つのファイルを、FlashAirのルートフォルダにコピーしておきます。
 
 つぎに、FlashAirのルートフォルダに以下のコードを記述したテキストファイル(ai_speaker_sample.lua)をコピーします。

drv8830sample.lua
-- FlashAir W-04 + DRV8830 + AzureIoTHub + IFTTT + GoogleHome
require "FaTimestamp"
require "FaUtil"
require "FaAzureIoTSAS"

local md_addr = 0x64 --モータードライバのI2Cアドレス(7bit表記)
STATE_SPEED = 0
STATE_TR = 1

-- config
local iotName = "ここにIoTHubの名称を記載する"
local signingKey = "ここに共有アクセスキー(主キー)を記載する"
local policy = "iothubowner"
local expire = 86400
local deviceName = "ここにデバイス名を記載する"

-- create SAS
local timestamp = getTimestamp()
local expires = timestamp + expire
local resourceUri = iotName ..".azure-devices.net"
local auth = sas.create(fa, resourceUri, signingKey, expires, policy)

--Azure IoT HubのC2Dメッセージの読み込みと完了通知
function getIoTHubMessage()
	local b,c,h = fa.request{url="https://"..iotName..".azure-devices.net/devices/"..deviceName.."/messages/deviceBound?api-version=2016-11-14",
		method = "GET",
		headers = {	["Authorization"]  = auth,
					["Content-Type"]   = "application/json"
		},
	}
	if(c ~= 200) then return end
	
	setSharedMem(b) --取得したメッセージを共有メモリに書き込み
	
	--ヘッダー内のETag部分を抜き出して、完了通知を送信
	local spos, epos = string.find(h, "ETag:")
	if(spos ~= nil) then
		local etag = string.sub(h, spos+7, spos+42)
		local b,c,h = fa.request{url="https://"..iotName..".azure-devices.net/devices/"..deviceName.."/messages/deviceBound/"..etag.."?api-version=2016-11-14",
			method = "DELETE",
			headers = {	["Authorization"]  = auth,
						["Content-Type"]   = "application/json"
			},
		}
	end
end

--I2C通信でデータを送信する
function write_i2c_command(addr, data1, data2)
	res = fa.i2c{ mode="start", address=addr, direction="write" }
	res = fa.i2c{ mode="write", data=data1 }
	res = fa.i2c{ mode="write", data=data2 }
	res = fa.i2c{ mode="stop"}
end

--モータードライバーにI2Cコマンドを送信する
function sendMotorDrive(addr, reg, vset, data)
	local vdata = bit32.bor(bit32.lshift(vset, 2), data)
	write_i2c_command(addr, reg, vdata)
end

--スピード:speednum、方向:trを指定してモータードライバーを駆動する
function controlSpeed(speednum, tr)
	local tr_reg = 0x00
	if(speednum < 0) then return end
	if(speednum > 200) then return end
	if (tr == 1) then
		tr_reg = 0x01
	else
		tr_reg = 0x02
	end
	if(speednum == 0) then
		sendMotorDrive(md_addr, 0x00, 0x00, 0x03)
    else
	    sendMotorDrive(md_addr, 0x00, (speednum/10)*3+3, tr_reg)
	end
end

--共有メモリからデータを取得する
function getSharedMem()
  local b = fa.sharedmemory("read", 0, 4)
  if (b == nil) then
    return 0
  else
    STATE_SPEED = tonumber(string.sub(b, 1, 3))
    STATE_TR  = tonumber(string.sub(b, 4, 4))
  end
  return 1
end

--共有メモリにデータを書き込む
function setSharedMem(cmd)
  local c = fa.sharedmemory("write", 0, 13, cmd)
  if (c ~= 1) then
    return 0
  end
  return 1
end

--モータードライバーを初期化、念のため速度0でコマンド送信
res = fa.i2c{ mode="init", freq="100" }
sendMotorDrive(md_addr, 0x00, 0x00, 0x00)

--共有メモリを初期化しておきます
local r = setSharedMem("0000000000000")
if(r ~= 1) then
  return
end
sleep(1000)
--1秒周期にAzure IoT hubのC2Dメッセージを確認
--共有メモリの情報を読み込んでモータドライバーを駆動します。
while(1) do
  local tmp_spd = STATE_SPEED
  local tmp_tr = STATE_TR
  getIoTHubMessage()
  r = getSharedMem()
  if(r == 1) then
    if(tmp_spd ~= STATE_SPEED or tmp_tr ~= STATE_TR) then
      controlSpeed(STATE_SPEED, STATE_TR)
    end
  end
  sleep(1000)
  collectgarbage("collect")
end

モータードライバー

 今回使用したモータードライバーとFlashAirの接続は、こちらの記事「FlashAir W-04のI2C機能で、DRV8830を駆動させてみた」を参照してください。

動作確認

 いろいろな要素を組み合わせたので大変でしたが、これで動くはずです。さっそく試してみましょう。
 LUISで設定したインテントに沿って、Google Homeに命令してみましょう。
 IFTTTで設定した呼びかける特定ワードをつけるのを忘れずに。

 「ねぇ、グーグル。フラッシュエアー、速度150で前に進め」……模型が前に進みます
 「ねぇ、グーグル。フラッシュエアー、速度ゼロで前に進め」……模型が止まります
 「ねぇ、グーグル。フラッシュエアー、速度100で後ろに進め」……模型が後ろに進みます
 「ねぇ、グーグル。フラッシュエアー、止まれ」……模型が止まります。
 インテントに該当しない文言/文章は、速度0になるようにAzure Functionsのプログラムで指定しているため止まるのです。

まとめ

 たった数センチ離れたところに置いたAIスピーカーとFlashAirを連携させるために、クラウドサービスを渡り歩くことになりましたが、無事、連携させることができました。今回は鉄道模型をコントロールしていますが、コントロール対象は工夫次第でなんでもできるでしょう。音声認識によるデバイスコントロールを、Azureの各サービスを使って実現してみてください。

参考資料

 FlashAir W-04について
 第4世代FlashAir W-04
 FlashAir開発者向け非公式wiki - W-04特設ページ
 Lua関数リファレンス
 Lua関数リファレンス - I2C

 FlashAirとAzure IoT Hubの連携について
 FlashAir_AzureIoT
 FlashAir_Timestamp

 Azure関連
 「世界一やさしい「Logic Apps」の操作解説」

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?