FlashAirからAmazon AWSに接続する2(実行編)

最終更新: 2018/10

このチュートリアルではFlashAirに保存されたデータを、インターネット経由で直接AWSに送信する方法を解説します。準備編はこちらをご覧下さい。

AWSの設定

実際にAWSアカウント作成からバケット作成、FlashAirから画像をアップロードするところまで実行してみましょう。AWS内での設定のほとんどは公式サイトに詳しい説明があるので、本チュートリアルでは簡易的にまとめています。

既にアカウント作成済み、S3に他アプリからアップロードを行ったことがある場合はFlashAirの設定に進んでください。

AWSアカウントを作成

AWS アカウントの作成 からアカウントを作成を行います。

連絡先情報、クレジットカード情報登録、アカウント認証、AWSサポートプランの選択を行いアカウントを作成します。詳細はAWS アカウント作成の流れ をご覧ください。
必要に応じてAWS セキュリティの認証情報 等を設定してください。

S3にバケットを作成する

Amazon S3 にログインし、「Amazon S3 の使用を開始する」を選択します。画面の指示通りに進みます。

「バケットを作成する」ボタンをクリックします。

ダイアログからバケットを作成します。任意のバケット名、リージョンを入力し「作成」ボタンをクリックします。

    • flashair-bkt
    • アジアパシフィック(東京)

S3へファイルをアップロードするために必要なアクセスキーとシークレットキーを取得します。
AWSメニューバーからアカウント名をクリックし、「セキュリティ認証情報」をクリックします。

IAM ユーザーのアクセスキーの管理 を参考に、アクセスキー、シークレットキーを取得、控えておきます。これらの情報は取扱に注意し、他人と共有しないようにしてください。

これでS3の準備が完了しました。

FlashAirの設定

FlashAirの設定を行います。FlashAirをステーションモードにして無線LAN子機として使用します。ステーションモードの詳細はステーションモードの利用をご覧ください。

CONFIGの設定

CONFIGを編集するには、/SD_WLAN/CONFIGをエディタ等で開き、前述のパラメータを編集します。 このフォルダは隠しフォルダとなっていますので、隠しフォルダを扱う事が出来るツールを使いましょう。
(Macの場合は /Volumes/(ボリュームラベル名)/SD_WLAN/CONFIGです。)

CONFIG内で以下の3つの情報を変更する必要があります。 括弧内は CONFIGの対応するパラメータ名です。
パラメータが存在しない場合は新しく行を追加してください。 パラメータの順序は問いません。

動作モード (APPMODE)
パラメータに 5を指定し、インフラストラクチャ・モードのステーション動作に変更します。
無線LAN SSID (APPSSID)
接続先無線LANのSSIDを指定します。
無線LAN ネットワークパスワード (APPNETWORKKEY)
接続先無線LANのネットワークキーを指定します。

編集後は、たとえば下記のようになります。

APPMODE=5
APPNAME=flashair
APPSSID=FOOSSID
APPNETWORKKEY=password0123
CIPATH=/DCIM/100__TSB/FA000001.JPG
VERSION=F15DBW3BW4.00.03
CID=02544d535730384708c00b7d7800d201
PRODUCT=FlashAir
VENDOR=TOSHIBA
MASTERCODE=18002d4ff0a2
LOCK=1

ファイルの設置、設定

ページ下部のサンプルコードをダウンロードし、FlashAirのルートフォルダ内に展開します。

s3-put.luaをテキストエディタで開きます。
3-6行目をバケット作成時のリージョン、バケット名、「S3にバケットを作成する」で取得したアクセスキー、シークレットキーで上書きします。

s3-put.lua

local s3Util = require("s3-util")

local REGION = "ap-northeast-1"					--リージョン名
local ACCESS_KEY = "AKIAIXXXXXXXXXXXXXXX"			--アクセスキー
local SECRET_KEY = "/id3EXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+"	--シークレットキー
local BACKET = "flashair-bkt"					--バケット名

保存するファイルについて

  • DATAフォルダ
    アップロードするファイルを格納するフォルダです。
  • s3-exec.lua
    sendtime.datファイルに保存されている日付以降に作成されたファイルがDATAフォルダ内に見つかった場合、s3-put.luaファイルのS3Putクラスにファイルパスとファイル名を引渡します。10秒ごとにフォルダ内をチェックします。
  • s3-put.lua
    S3のバケットにファイルをアップロードします。
  • s3-util.lua
    ユーティリティクラスです。現在時刻、ハッシュ値等を、計算・取得します。
  • sendtime.dat
    s3-exec.luaでファイルを検索した後に現在日付を保存します。
    注意 ファイルがアップロードされない場合は、ファイル内に1を記入(ファイル検索用日時の初期化)し上書き保存してから再度DATAフォルダにファイルを作成、コピーしてください。

それでは、サンプルプログラムがどのように動作するかをみていきます。

s3-exec.lua

local s3Put = require("s3-put")

local dir = "/DATA"
  • 3行目
    s3-put.luaをインクルードします。
  • 5行目
    アップロードするファイルの格納フォルダを指定します。変更する場合は"/DATA"を任意の場所に修正してください。
function readSendTime()
    local sendtime = 0
    local file = io.open(timefile, "r")
    if file ~= nil then
        sendtime = file:read()
        file.close()
    else
	debug("file == nil")
    end
    if sendtime == nil then
	debug("sendtime == nil")
        sendtime = 0
    end 
    debug("r = " .. sendtime)
    return sendtime
end

sendtime.datファイルの内容を読み込み、内容を返します。

function writeSendTime(sendtime)
    local file = io.open(timefile, "w+")
    if file ~= nil then
        file:write(sendtime)
        file:close()
        debug("w = " .. sendtime)
    end
end

sendtime.datファイルにファイル検索を行った日時を書き込みます。

function getFilePath(sendtime)
	local filepath, filename = nil
	local result, filelist, sendtime = fa.search("file", dir, sendtime)
	if result == 1 or filelist ~= nil then
		filepath = string.match(filelist, "(.-),")
		if filepath ~= nil then
			filename = string.match(filepath, "[^/]*$")
		end
	end
	return filepath, filename, sendtime 
end

fa.searchで、引数sendtimeと更新日時が一致するファイル、若しくは引数sendtimeより新しいファイルのうち最も古いファイル(指定された日時の次に新しいファイル)のファイルを取得し、返します。

function execute()
    local temptime = readSendTime()
    if temptime == 0 then
        print("temptime == 0")
        return
    end
    local sendtime = tonumber(readSendTime()) + 1
    local filepath, filename, time = getFilePath(sendtime)
    if filepath == nil then
        return
    end
    writeSendTime(time)
    debug("filepath = " .. filepath .. ", filename = " .. filename)
    local s3 = s3Put:new(filepath, filename)
    s3:put()
end
  • 47行目
    sendtime.datファイルを読み込みます。日時が取得できなかった場合は終了します。
  • 52-53行目
    sendtime.datに保存されている日時の0.5秒後以降のファイルを検索します。
  • 57行目
    該当ファイルが見つかった場合、ファイルの更新日付をsendtime.datに書き込みます。
  • 59行目
    S3Putクラスにファイルパスとファイル名を引き渡します。
function debug(msg)
	--print(msg)
end

デバッグメッセージを表示します。必要な場合はコメントを削除してください。

while(1) do
	execute()
	sleep(10000)
	collectgarbage("collect")
end

sleepを使用し、10秒毎にファイル検索を行います。

s3-put.lua

local s3Util = require("s3-util")

s3-util.luaをインクルードします。

local S3Put = {}

S3Put.new = function(self, fpath, fname)
	local this = {}
	
	local SERVICE = "s3"
	local METHOD = "PUT"
	local PAYLOAD_HASH = "UNSIGNED-PAYLOAD"
	local ALGORITHM = "AWS4-HMAC-SHA256"
	local CONTENT_TYPE = "image/jpeg"

	local util = s3Util:new()
	
	local filepath = fpath
	local filename = fname
	local nowtime = util:currentTime()
	local date = os.date('!%Y%m%d', nowtime)
	local datetime = os.date('!%Y%m%dT%H%M00Z', nowtime)

	local dataPath = "/" .. filename
	local host = SERVICE .. "-" .. REGION .. ".amazonaws.com"
	local canonicalURI = "/" .. BACKET .. dataPath
	local endpoint = "https://" .. host
	local canonicalQuery = ""
	local signedHeader = "content-type;host;x-amz-content-sha256;x-amz-date"
	local credentialScope = date .. "/" .. REGION .. "/" .. SERVICE .. "/" .. "aws4_request"
			
	function this:getSignatureKey()
		local kDate = util:sha256Hmac("AWS4" .. SECRET_KEY, date)
		debug("kDate=" .. kDate)
		kDate = util:hex2Bytes(kDate)
		local kRegion = util:sha256Hmac(kDate, REGION)
		debug("kRegion=" .. kRegion)
		kRegion = util:hex2Bytes(kRegion)
		local kService = util:sha256Hmac(kRegion, SERVICE)
		debug("kService=" .. kService)
		kService = util:hex2Bytes(kService)
		local kSigning = util:sha256Hmac(kService, "aws4_request")
		debug("kSigning=" .. kSigning)
		kSigning = util:hex2Bytes(kSigning)
		return kSigning
	end

	function this:getStringToSign()
		local canonicalHeader = "content-type:" .. CONTENT_TYPE .. "\n" .. "host:" .. host .. "\n" .. "x-amz-content-sha256:" .. PAYLOAD_HASH .. "\n" .. "x-amz-date:" .. datetime .. "\n"
		local canonicalRequest = METHOD .. "\n" .. canonicalURI .. "\n" .. canonicalQuery .. "\n" .. canonicalHeader .. "\n" .. signedHeader .. "\n" .. PAYLOAD_HASH
		debug("canonicalRequest=" .. canonicalRequest)
		local stringToSign = ALGORITHM .. "\n" .. datetime .. "\n" .. credentialScope .. "\n" .. util:sha256(canonicalRequest)
		debug("----stringToSign----")
		debug(stringToSign)
		debug("----------")
		return stringToSign
	end

	function this:getAuthorizationHeader()
		local signingKey = this:getSignatureKey()
		local signingSign = this:getStringToSign()
		local signature = util:sha256Hmac(signingKey, signingSign)
		local authorizationHeader = ALGORITHM .. " " .. "Credential=" .. ACCESS_KEY .. "/" .. credentialScope .. ", " .. "SignedHeaders=" .. signedHeader .. ", " .. "Signature=" .. signature
		debug("----authorizationHeader----")
		debug(authorizationHeader)
		debug("----------")
		return authorizationHeader
	end

	function this:put()
		local filesize = lfs.attributes(filepath, "size")
		local headers = {["content-type"]=CONTENT_TYPE, ["content-length"]=filesize, ["x-amz-date"]=datetime, ["x-amz-content-sha256"]=PAYLOAD_HASH, ["Authorization"]=this:getAuthorizationHeader()}
		local requestURL = endpoint .. canonicalURI
		local body, code, header = fa.request{
			url=requestURL,
			method="PUT",
			headers=headers,
			file=filepath,
			body='<!--WLANSDFILE-->',
			bufsize=1460*10,
		}

		debug("resultCode=" .. code)
		debug("headers=" ..  cjson.encode(header))
		debug("body=" .. body)
	end 
	return this
end

S3Putクラスを定義します。

  • 35-49行目
    シークレットキー、リージョン等から署名を暗号化するためのキーを作成します。署名キーの詳細はこちら をご覧ください。
  • 51-60行目
    正規リクエストを作成します。正規リクエストの詳細はこちら をご覧ください。
  • 62-71行目
    署名キーと正規リクエストを用いて、署名バージョン4の署名文字列を作成し、正規ヘッダーを作成します。
  • 73-89行目
    fa.requestを使用してAWS APIへリクエストを送信します。

実行、結果

FlashAirの設定が終わったら、FlashAirを抜き差しして修正を反映させます。

書き込み後は必ずカードをいったん抜いて再挿入するなどしてSDメモリカードホスト機器に再認識させてください。
PCなどのSDメモリカードホスト側のOSがSDメモリカードの内容をキャッシュしていると、その変更をOSが認識する事が出来ません。 その為、SDメモリカードホスト機器とLuaやCGIから同時に変更を行うとFAT不整合が起きる可能性があります。

ブラウザから http://flashair/s3-exec.lua を実行します。

DATAフォルダにファイルを作成またはコピーします。

アップロードが行われたか確認します。
S3にバケットを作成する」で作成したバケット名をクリックします。

DATAフォルダ内のファイルがアップロードされていることが確認できました。

応用

準備編で解説したLambda、Rekognition、QuickSightを使用すると、取得したデータを加工、可視化、共有することが出来ます。

今回は、動体検知カメラで撮影した顔写真から年齢や性別、笑顔の有無などをRekognitionにより判定し、QuickSightで可視化してみました。

Lambda内でS3にファイルが追加されたらRekognitionの顔認識処理を呼び出す関数を作成します。関数の作成方法はLambda 関数を作成する をご覧ください。
あらかじめS3内に解析結果を格納するためのバケットを準備しておきます。Rekognitionの解析処理を取得したらS3に解析結果を解析バケットに格納する関数も用意します。

QuickSightでは、上記で作成した解析バケットのデータを用いてグラフを作成することが出来ます。QuickSightの使用方法はAmazon QuickSight とは をご覧ください。

Lambda
QuickSight

おわりに

2回に渡りAWSに直接FlashAirのLuaスクリプトから接続する方法を解説しましたが、今回行った顔認識以外にも、FlashAir単体では難しい様々なコンピューティング処理をAWS側で行うことが可能です。

すでにSDカードスロットのある機器をお使いの場合は、WiFi環境があればすぐにIoTシステムを構築することができます。

「IoTスターターパック」(株式会社KYOSO様)
同様のコンセプトで、FlashAirに書き込んだファイルをAmazon S3に書き込むことができます。LTEルータを使用するためWiFi環境のない屋外でも利用することができます。CF(コンパクトフラッシュ)対応の機器でも、CF/SD変換アダプタを介して利用することが可能です。詳細はこちらをご覧ください。
FlashAir IoT Hub
コンピューティング処理は必要なく、データの蓄積のみ行う場合や、SNSや他のウェブサービスと連携したい場合はFlashAir IoT Hubが便利です。簡易な設定だけで写真や計測値のクラウド管理からビジュアライズ、またIFTTT連携を行うことができます。詳細はこちら をご覧ください。

サンプルコード

advanced_tutorial_08.zip (4KB)

このサイトのサンプルコードは 二条項BSDライセンスで提供されています。