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

このチュートリアルでは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を抜き差しして修正を反映させます。

ブラウザから 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 Lambda
QuickSight QuickSight


サンプルコード

advanced_tutorial_08.zip (4KB)

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