Atom's tech blog

Pythonを勉強しよう! 変数名の命名規則

f:id:iAtom:20201106203548j:plain

Python初心者の備忘録.....

今回は、自身スキル不足で命名規則を詳しく知らなかった....情けない。 プログラミング言語では下記の命名規則が広く使われているみたいです。

命名規則

記法 規則説明 記述例
キャメル記法 複数の単語を連結。先頭文字は小文字後に続く単語の先頭は大文字で記述。単語と単語の間に空白や連結記号は使わない。 serverData, faceDetect
パスカル記法 数の単語を連結。すべての単語の先頭文字を大文字で記述。単語と単語の間に空白や連結記号は使わない。 ServerData,, FaceDetect
スネーク記法 複数の単語を連結。単語すべて小文字で記述し、単語の間に「_」アンダーバーを記述 server_data,, face_detect

感想

いろいろソースコードを掲載したが、筆者自身どの記法にも合わない。まー、全く意識していないから仕方ないか。。。。

今度からは意識して見やすいコードを書くことを心がけることにします。

Pythonを勉強しよう! シングル/ダブル/トリプルクォーテーションについて

f:id:iAtom:20201106203500j:plain

Python初心者の備忘録.....

今回は文字型には、「シングルクォーテーション」、「ダブルクォーテーション」、「トリプルクォーテーション」があり、このあたり勉強してします。

シングルクォーテーション/ダブルクォーテーション

」:シングルクォーテーション、「"」ダブルクォーテーションのどちらを使っても良いです。

文字列にしたい先頭と最後に記述します。

以下のケースでは「Atom」まで文字列になってしまうため、ダブルクォーテーションを使用する。

全文文字列にならない
name = 'Atom's tech blog'
全文文字列にする
name = "Atom's tech blog"
参考

ダブルクォーテーションが内側にある場合は、シングルクォーテーション、ダブルクォーテーションで囲む。

print('<a href="http://www.example.com"></a>')
print("<a href=\"http://www.example.com\"></a>")

# <a href="http://www.example.com"></a>
# <a href="http://www.example.com"></a>

トリプルクォーテーション

下記のように文字列が継続して改行している場合でも、文字列の続きとして扱われます。

改行している文字列自体もそのままの状態で扱われるため、複数行として表示します。

name ='''Atom's tech blog
test
proglam'''

print(name)

# Atom's tech blog
# test
# proglam

Pythonを勉強しよう! 文字列から数値への変換

f:id:iAtom:20201106203424j:plain

Python初心者の備忘録.....

今回は数値の文字列から数値へ変換することを勉強してみます。

数字の文字列を整数(int)、浮動小数点(floar)へ変換

Pythonで数字の文字列を整数値にはint()、小数点ありの数字文字列を浮動小数点に変換するにはfloat()を使用します。

Pythonは整数値と文字列の数字は直接演算できません。

そこで文字列の数字を整数値に変換することで演算が可能となります。

print(int('500'))
print(float('100.1'))
print(float('500'))

# 500
# 100.1
#500.0

エラーになるケース

文字列に小数点がある場合

print(int('500.1'))

#raceback (most recent call last):
#  File "<stdin>", line 1, in <module>
#ValueError: invalid literal for int() with base 10: '500.1'

文字列のため、数字間に「,」(カンマ)が入るケースがあるので、これもエラーとなる

print(int('50,010'))

#raceback (most recent call last):
#  File "<stdin>", line 1, in <module>
#ValueError: invalid literal for int() with base 10: '50,010'

回避するには文字列から「,」カンマを削除しよう。 replace()メソッドで削除(空に置換)変換可能です。

print(int('50,010'.replace(',', '')))

#50010

2、8、10、16進数文字列を10進数に変換

前回も説明を書いていたが再度。こちらもint()を使用することで可能です。

第一パラメータには2、8、10、16進数の文字列。第二パラメータには基数「2、8、10、16」

# 変数xに文字列の代入
x = '100'
# 2進数に変換し、結果を出力
print(int(x,2))
# 8進数に変換し、結果を出力
print(int(x,8))
# 10進数に変換し、結果を出力
print(int(x,10))
# 16進数に変換し、結果を出力
print(int(x,16))

#4
#64
#100
#256

下記の16進数の文字列の場合は、大文字小文字どちらでも変換可能で、「0x」の文字列でも問題なし。

# 変数に文字列の代入
big = 'FF'
small ='ff'
str_hex = '0xff' 
# 16進数に変換し、結果を出力
print(int(big,16))
print(int(small,16))
print(int(str_hex,16))
print(int(str_hex,0))

# 255
# 255
# 255
# 255

数字の文字列を変換して演算

演算するときに数値と数値の演算は可能ですが、数値と文字列の演算はできません。

よく使われるケースは、ログに出力した結果が数値の文字列の場合があり、そのままの取得したデータで演算するとエラーが発生します。

その場合は、文字列を数値に変換して演算を行います。

下記サンプルソースはlog_valueが文字列のため、int()で10進数の整数に変換し、forでループして演算しています。

log_value = '1'
for count in range(5):
     print( count + int(log_value,10))

#1
#2
#3
#4
#5

Pythonを勉強しよう! 扱うデータの種類とn進数の変換

f:id:iAtom:20201106202255j:plain

Python初心者の備忘録.....

今回はPythonで扱う基本データ型とn進数の変換について勉強してみます。

Pythonで扱う基本データ型

データの種類型 値の例 説明
整数型 int型 123 , 3 , -8 intとはinteger(整数)として、小数点を含まないものを示す
整数型 float型 3.14159, -0.34 floatとは浮動小数点数として、小数点を含むものを示す
文字列型 str型 ポイント, point 文字列は「'」(シングルクォーテーション )、「"」(ダブルクォーテーション)で囲んだ文字列
論理型 bool型 True,False 真を示すときは「True」偽を示すときは「False」の2つ値を使う

上記のint型の値の例は、10進数で表しています。数字の前に「-」(マイナス)を入れることで負の値として扱われます。

プログラミングでは10進数だけではなく、2進数、8進数、16進数をよく使用します。

次は2進数、8進数、16進数の書き方を説明します。

2進数、8進数、16進数の書き方

進数 表記例 説明
2進数 0b100,0B111000 先頭に「0b」または「0B」をつけます
8進数 0o100,0O111000 先頭に「0o」または「0O」をつけます
16進数 0x6,0x1f 先頭に「0x」または「0X」をつけます

書き方はわかったが、よく使いたくなるのが10進数で表した数値を「2進数、8進数、16進数」に変換する方法です。

2進数、8進数、16進数へ変換

bin(),oct(),hex()を使用

10進数の整数値を、2進数、8進数、16進数へ変換します。

変換する進数 関数 入力パラメータ説明 出力型
2進数 bin() bin関数のパラメータ値に10進数 文字列型
8進数 oct() oct関数のパラメータ値に10進数 文字列型
16進数 hex() hex関数のパラメータ値に10進数 文字列型
# 変数xに10(10進数)を代入
x = 10
# 2進数に変換し、結果を出力
print(bin(x))
# 8進数に変換し、結果を出力
print(oct(x))
# 16進数に変換し、結果を出力
print(hex(x))

# 0b1010
# 0o12
# 0xa

format()を使用

10進数の整数値を、2進数、8進数、16進数へ変換します。

変換する進数 関数 入力パラメータ説明 出力型
2進数 format() format関数の第一パラメータ1に10進数の数値、第二パラメータに'b' 文字列型
8進数 format() format関数の第一パラメータ1に10進数の数値、第二パラメータに'o' 文字列型
16進数 format() format関数の第一パラメータ1に10進数の数値、第二パラメータに'x' 文字列型
# 変数xに10(10進数)を代入
x = 10
# 2進数に変換し、結果を出力
print(format(x,'b'))
# 8進数に変換し、結果を出力
print(format(x,'o'))
# 16進数に変換し、結果を出力
print(format(x,'x'))

# 1010
# 12
# a

10進数へ変換

int()を使用

文字列の2,8,10,16進数を10進数に変換します。

変換する進数 関数 入力パラメータ説明 出力型
2進数 int() format関数の第一パラメータに文字列の2進数の数字、第二パラメータに2 整数型
8進数 int() format関数の第一パラメータに文字列の8進数の数字、第二パラメータに6 整数型
10進数 int() format関数の第一パラメータに文字列の10進数の数字、第二パラメータに10 整数型
16進数 int() format関数の第一パラメータに文字列の16進数の数字、第二パラメータに16 整数型
# 変数xに文字列の代入
x = '100'
# 2進数に変換し、結果を出力
print(int(x,2))
# 8進数に変換し、結果を出力
print(int(x,8))
# 10進数に変換し、結果を出力
print(int(x,10))
# 16進数に変換し、結果を出力
print(int(x,16))

# 4
# 64
# 100
# 256
小数点を含む場合は、小数点以下が切り捨てられる
print(int( 3.1415))

#3

AWS IoT Device Shadowを使ってみた!(Part4)

f:id:iAtom:20201218174417j:plain


本記事は、最終目標である遠隔からDevice Shadow機能を使ってIoT Device制御をまとめたものです。 なお、4つの記事の中で今回は 下記の「 Part4 」です

主な全体記事の流れです。

■ Part1 : API Gatewayから起動するLambda関数の作成

■ Part2:API GatewayAPI作成

■ Part3 : AWS IoT Deviceとshadow作成

■ Part4 : iPhoneアプリ作成 と 遠隔Camera起動確認

iPhoneアプリも初心者で......。

プロジェクトの作り方や、viewイメージで作成したボタンをソースコード接続や、ボタンをタップしたメソッド生成方法..... などは割愛します。

注意

2020/12/17からboto3でIoT CoreのDevice ShadowにアクセスするとSSLエラーが発生する事象が出ています。

AWSのcertifiライブラリが「2020.12.5」にバージョンアップされたことが原因らしく、下記サイトを参考に応急処置を行い、問題なく動作できることを確認しました。

その時のエラーメッセージです。

  {"errorMessage": "SSL validation failed for https://data.iot.ap-northeast-1.amazonaws.com/things/RaspberryPi_Camera/shadow [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate


応急処置方法のサイトをリンク貼り付けます。

certifiライブラリ「2020.11.8」版をMACで作成し、その「 layer.zip 」ファイルをAWS Lambdaコンソールから「 レイヤー追加 」、対象となるLambda関数に適用でいけました。


dev.classmethod.jp


正式にはIoT CoreのATSエンドポイント指定することで対応可能なようです。 すみません。自分は確認していません。ご了承ください。

dev.classmethod.jp

AWS構成図

赤点線で囲んだ箇所を今回作成します。


f:id:iAtom:20201217153955j:plain

Appプロジェクト環境

使用するinterfaceは「 Storyboard 」、言語(Language)は「 Swift 」を使用します。


f:id:iAtom:20201217131223j:plain

Alamofireインストール

Alamofireとは

AlamofireはiOSアプリでネットワーク通信(HTTP、REST APIなど)を簡単に使うことができるライブラリです。ネイティブのネットワーク通信のライブラリを使用するより簡単に実装ができるようです。

アプリ作成初心者にはおすすめらしいので「 Alamofire 」を使用します。

インストール

いろいろなサイトでインストール方法が掲載されていますが、一番簡単な方法でやります。

Xcodeの「 File 」 -> 「 Swift Packages 」 -> 「 Add Package Dependency.... 」を選択します。


f:id:iAtom:20201217131720j:plain


Alamofireのgitリポジトリアドレスを入力 「 https://github.com/Alamofire/Alamofire 」を入力し、「 Next 」ボタンをクリックします。


f:id:iAtom:20201217131850j:plain


Branch 」のラジオボタンで選択し、「 Next 」ボタンをクリックします。


f:id:iAtom:20201217132014j:plain


Alamofireのチェックボックスにチェックを入れて、「 Next 」ボタンをクリックします。


f:id:iAtom:20201217132151j:plain


Alamofireが表示されている状態を確認します。


f:id:iAtom:20201217132520j:plain


これでAlamofireがインストールできました。

アプリソースコード

冒頭でも説明しましたが、viewイメージで作成したボタンをソースコード接続や、ボタンをタップなどのメソッド生成方法の図解は割愛します。

Main.storyboardファイルにボタン、テキストをGUI上で配置しておいてください

ソースファイルのコピペだけではエラーとなりますので、ご注意ください。

ViewController.swift

単純な「ON」と「OFF」ボタンを配置し、タップ動作を入れているだけのシンプル? な初心者作成ソースコードです。

import UIKit
import Alamofire

class ViewController: UIViewController {

    @IBOutlet weak var buttonON: UIButton!
    @IBOutlet weak var buttonOff: UIButton!

    //  AWS API Gateywayで設定し生成されたCamera ONのPOST URL
    //  ■ xxxxxxxxxxxは各自の環境に合わせてください。
    var awsApiGatewayURLOn = "https://xxxxxxxxxx.execute-api.xxxxxxxxxxxxxxx.amazonaws.com/development/test?camera=1"
    //  AWS API Gateywayで設定し生成されたCamera OFF POST URL
    //  ■ xxxxxxxxxxxは各自の環境に合わせてください。
    var awsApiGatewayURLOff =
        "https://xxxxxxxxxxxxx.execute-api.ap-xxxxxxxxxxxx.amazonaws.com/development/test?camera=0"
    //  AWS API Gateywayで生成したAPIキー
    //  ■ xxxxxxxxxxxは各自の環境に合わせてください。
    var awsApiKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

    override func viewDidLoad() {
        super.viewDidLoad()
        
    }

    override func viewDidAppear(_ animated: Bool) {
       //  ボタンONの形を変える
        buttonON.frame = CGRect(x:25,y:131,width:330,height:150)
        buttonON.layer.masksToBounds = false
        buttonON.layer.cornerRadius = 10.0
        // ボタンの影
        buttonON.layer.shadowOffset = CGSize(width: 3, height: 3)// 影の方向
        buttonON.layer.shadowOpacity = 0.4 // 影の濃さ
        buttonON.layer.shadowRadius = 7 // 影のぼかし量

        //  ボタンOFFの形を変える
        buttonOff.frame = CGRect(x:25,y:300,width:330,height:150)
        buttonOff.layer.masksToBounds = false
        buttonOff.layer.cornerRadius = 10.0
        
        // ボタンの影
        buttonOff.layer.shadowOffset = CGSize(width: 3, height: 3)
        buttonOff.layer.shadowOpacity = 0.4
        buttonOff.layer.shadowRadius = 7
    }
    
    // ONボタンを押した状態の時に起動
    @IBAction func buttonON_touchdown(_ sender: UIButton) {
        
        // 色をsystemGray3に変える
        buttonON.backgroundColor = UIColor.systemGray3
    }
    
    // OFFボタンを押して離した時に起動
    @IBAction func buttonON_touchUp(_ sender: UIButton) {
        // 色をsystemYellowに変える
        buttonON.backgroundColor = UIColor.systemYellow
        
        // AWS Gatewayアクセス CameraON URL
        AF.request(awsApiGatewayURLOn,method: .post,headers: ["X-API-KEY":awsApiKey]).response { response in debugPrint(response)}
    }
    
    // OFFボタンを押した状態の時に起動
    @IBAction func buttonOFF_touchdown(_ sender: UIButton) {
        // 色をsystemGray3に変える
        buttonOff.backgroundColor = UIColor.systemGray3

    }
    
    // OFFボタンを押して離した時に起動
    @IBAction func buttonOFF_touchUp(_ sender: UIButton) {
        // 色をsystemTealに変える
        buttonOff.backgroundColor = UIColor.systemTeal
        
        // AWS Gatewayアクセス CameraOFF URL
        AF.request(awsApiGatewayURLOff,method: .post,headers: ["X-API-KEY":awsApiKey]).response { response in debugPrint(response)}
        
    }
}

App起動

Xcodeでビルドし、起動するとON/OFFの2つのボタンを表示します。

下記イメージはMAC上で表示したSimulatorです。

実際に動作させるときは、iphoneにAppを転送しています。


f:id:iAtom:20201217135734j:plain


AWS設定やIoT Deviceソースコード、Lambda関数ソースコードiOS Appソースコードの説明は以上です。

これでiPhoneからIoT Deviceに接続しているカメラを起動する準備ができました。

それでは実際に試してみます。

App → IoT Core Shadow 動作

Appから「 ON 」ボタンを押してIoT CoreのShadowテーブルが更新することを確認します。 イメージは赤点線で囲んだ箇所のルートです。


f:id:iAtom:20201217140536j:plain

IoT CoreのShadowの状態確認

IoT Coreコンソールにアクセスします。

コンソール画面の「 管理 」-> 「 モノ」-> 「 RaspberryPi_Camera 」-> 「 シャドウ 」-> 「 classic shadow 」をクリックします。

"camera" : "0"はカメラ停止 "camera" : "1"はカメラ起動 です。


f:id:iAtom:20201218111035j:plain

Shadow状態確認

iOS Appから「 ON 」ボタンをクリックします。

Appから送信された「 camera=1 」のREST APIが、最終的にIoT Core Shadowに到達し、「 desired 」の「 "camera" : "1" 」に変化したことが確認できます。


f:id:iAtom:20201218111803j:plain


更に「 desired 」と「 reported 」のcamera変数の値が異なることをAWS shadowが検出し、「 delta 」で値をステータスとして表示しています。

このステータスの確認は、一回ブラウザの更新ボタンで画面更新で表示されます。


f:id:iAtom:20201218112946j:plain


次にiOS Appからの指示とIoT Deviceの連携を一連の動作で見たいので、一旦OFFボタンをタップして、「 desired 」の「 "camera" : "0" 」にします。

REST API制御

iOS Appを使用しなくても、直接REST APIを呼び出して制御することもできます。

x-api-key、httpsアドレスの 「xxxxxxxxxxxxxxxxxxxx」は各自の環境に合わせてください。

また「ap-northeast-1」.は、API Gatewayで作成したリージョン(今回は東京)です。

ON/OFFの違いは「 camera=0 」と 「 camera=1 」のみです。

Camera ONのAPI
   curl -X POST --header "x-api-key:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
   https://xxxxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/"test?camera=0"
Camera OFFのAPI
   curl -X POST --header "x-api-key:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
   https://xxxxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/"test?camera=1"

IoT Device shadow起動とiOS App制御

IoT DeviceがAWS IoT CoreにShadowにアクセスし、「 desired 」の「 "camera" : "1" 」に変化したことを検出し、「reported 」の「 "camera" : "1" 」に変化させ、更にIoT Deviceのカメラが起動していることを確認します。

赤点線部分の確認です。


f:id:iAtom:20201218113949j:plain

Pythonスクリプト実行

IoT Device(RaspberryPi)にアクセスし、shadow起動のPythonスクリプトを起動します。

    $ python3 shadow_camera_test.py


起動後、標準出力で表示ログを出力します。

下記の表示イメージは、全て「AWSIoTPythonSDK.MQTTLib」ライブラリで出力しています。

  2020-12-17 10:01:55,642 - AWSIoTPythonSDK.core.protocol.internal.clients - DEBUG - Initializing MQTT layer...
  2020-12-17 10:01:55,644 - AWSIoTPythonSDK.core.protocol.internal.clients - DEBUG - Registering internal event callbacks to MQTT layer...
  2020-12-17 10:01:55,644 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - MqttCore initialized
  2020-12-17 10:01:55,645 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Client id: RaspberryPi
  2020-12-17 10:01:55,645 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Protocol version: MQTTv3.1.1
  2020-12-17 10:01:55,645 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Authentication type: TLSv1.2 certificate based Mutual Auth.
  2020-12-17 10:01:55,645 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Configuring offline requests queueing: max queue size: 0
  2020-12-17 10:01:55,646 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Configuring offline requests queue draining interval: 0.100000 sec
  2020-12-17 10:01:55,646 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Configuring endpoint...
  2020-12-17 10:01:55,647 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Configuring certificates...
  2020-12-17 10:01:55,647 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Configuring reconnect back off timing...
  2020-12-17 10:01:55,648 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Base quiet time: 1.000000 sec
  2020-12-17 10:01:55,648 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Max quiet time: 32.000000 sec
  2020-12-17 10:01:55,648 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Stable connection time: 20.000000 sec
  2020-12-17 10:01:55,649 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Configuring connect/disconnect time out: 10.000000 sec
  2020-12-17 10:01:55,649 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Configuring MQTT operation time out: 5.000000 sec
  2020-12-17 10:01:55,649 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Performing sync connect...
  2020-12-17 10:01:55,649 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Performing async connect...
  2020-12-17 10:01:55,649 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Keep-alive: 600.000000 sec
  2020-12-17 10:01:55,650 - AWSIoTPythonSDK.core.protocol.internal.workers - DEBUG - Event consuming thread started
  2020-12-17 10:01:55,651 - AWSIoTPythonSDK.core.protocol.mqtt_core - DEBUG - Passing in general notification callbacks to internal client...
  2020-12-17 10:01:55,651 - AWSIoTPythonSDK.core.protocol.internal.clients - DEBUG - Filling in fixed event callbacks: CONNACK,DISCONNECT,MESSAGE
  2020-12-17 10:01:55,774 - AWSIoTPythonSDK.core.protocol.internal.clients - DEBUG - Starting network I/O thread...
  2020-12-17 10:01:55,848 - AWSIoTPythonSDK.core.protocol.internal.workers - DEBUG - Produced [connack] event
  2020-12-17 10:01:55,849 - AWSIoTPythonSDK.core.protocol.internal.workers - DEBUG - Dispatching [connack] event
  2020-12-17 10:01:55,850 - AWSIoTPythonSDK.core.protocol.internal.workers - DEBUG - No need for recovery
  2020-12-17 10:01:55,851 - AWSIoTPythonSDK.core.protocol.internal.clients - DEBUG - Invoking custom event callback...
  2020-12-17 10:01:55,852 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Performing sync subscribe...
  2020-12-17 10:01:55,852 - AWSIoTPythonSDK.core.protocol.internal.workers - DEBUG - Adding a new subscription record:$aws/things/RaspberryPi_Camera/shadow/update/delta qos: 0
  2020-12-17 10:01:55,853 - AWSIoTPythonSDK.core.protocol.internal.clients - DEBUG - Filling in custom suback event callback...
  2020-12-17 10:01:55,907 - AWSIoTPythonSDK.core.protocol.internal.workers - DEBUG - Produced [suback] event
  2020-12-17 10:01:55,908 - AWSIoTPythonSDK.core.protocol.internal.workers - DEBUG - Dispatching [suback] event
  2020-12-17 10:01:55,908 - AWSIoTPythonSDK.core.protocol.internal.clients - DEBUG - Invoking custom event callback...
  2020-12-17 10:01:55,909 - AWSIoTPythonSDK.core.protocol.internal.clients - DEBUG - This custom event callback is for pub/sub/unsub, removing itafter invocation...
  2020-12-17 10:01:57,912 - AWSIoTPythonSDK.core.shadow.deviceShadow - INFO - Subscribed to delta topic for deviceShadow:RaspberryPi_Camera
  2020-12-17 10:02:01,042 - AWSIoTPythonSDK.core.protocol.internal.workers - DEBUG - Produced [message] event
  2020-12-17 10:02:01,042 - AWSIoTPythonSDK.core.protocol.internal.workers - DEBUG - Dispatching [message] eventdelta/RaspberryPi_Camera


ASW IoT Coreコンソールからshadow状態の確認します。

まだカメラ停止状態です。


f:id:iAtom:20201218153144j:plain


iOS Appから「 ON 」ボタンをタップすると、「 desired 」の「 "camera" : "1" 」に変化したことを検出し、「reported 」の「 "camera" : "1" 」になりました。


f:id:iAtom:20201218163423j:plain


Iot Device(RaspberryPi)のshadow変化検出ログイメージです。


  ++++++++DELTA++++++++++
  camera: 1
  version: 12
  +++++++++++++++++++++++

  updating...


最後にカメラが起動した時の画像です。

時間がIoT Core コンソールのShadowステータスと異なりますが、ご了承ください。


f:id:iAtom:20201218163955j:plain

最後に

途中AWSアップデートで動作しない時もありましたが、目標としているAWS Iot Device Shadow機能を使って遠隔操作を確認することができました。

この方法は見守りカメラなどでも使用されているようですね。ご参考にしていただければと思います。

AWS IoT Device Shadowを使ってみた!(Part3)

f:id:iAtom:20201218174201j:plain


本記事は、最終目標である遠隔からDevice Shadow機能を使ってIoT Device制御をまとめたものです。 なお、4つの記事の中で今回は 下記の「 Part3 」です

主な全体記事の流れです。

■ Part1 : API Gatewayから起動するLambda関数の作成

■ Part2:API GatewayAPI作成

■ Part3 : AWS IoT Deviceとshadow作成

■ Part4 : iPhoneアプリ作成 と 遠隔Camera起動確認

AWS構成図

赤点線で囲んだ箇所を今回作成します。


f:id:iAtom:20201217153612j:plain

AWS IoTアクセス

AWS IoTコンソールにアクセスします。


f:id:iAtom:20201216165626j:plain

IoTポリシーの作成

AWS IoTのコンソールの左メニュの「 安全性」-> 「 ポリシー 」をクリックします。

ポリシー登録画面に遷移し、ポリシーを登録します。「作成 」ボタンをクリックし、ポリシーの作成画面に遷移します。

名前 」、「 アクション」、「リソースARN」、「 効果 ]を入力&選択します。今回は名前は「 RaspberryPi_Camera-Policy 」にしています。


f:id:iAtom:20201216170458j:plain

モノの作成

AWS IoTのコンソールの左メニュの「 管理 」-> 「 モノ 」をクリックします。

モノの登録画面遷移し、IoT Deviceを登録します。「 作成ボタン 」をクリックします。


f:id:iAtom:20201216165854j:plain


単一のAWS IoTモノを作成します。「 単一のモノを作成する 」ボタンをクリックします。


f:id:iAtom:20201216170756j:plain


名前 」、「 Thing Shadow 」を入力します。今回はIoT Deviceは「Raspberry Pi」を使用するため「RaspberryPi_Camera」としています。

「Thing Shadow」はすでに設定内容が 「 "Welcome...... "」となっていますが、下記のように書き換えます。

最後に「 次へ 」ボタンをクリックします。


f:id:iAtom:20201216171256j:plain


画像が見えないので「Thing Shadow」は下記をご参考にしてください。

 {
     "desired": {
         "camera": "0"
      },
      "reported": {
         "camera": "0"
 },


証明書を作成します。「 __ 証明書を作成」ボタンをクリックします。


f:id:iAtom:20201216171930j:plain


証明書、キーが発行されますので、ダウンロードしておきます。この証明書は、RaspberryPiにコピーして使用します。

ルートCAファイルは、別途RaspberryPiからダウンロードするため、ここではダウンロードはしません。

証明書、キーファイルのダウンロード完了後に「 有効化 」ボタンをクリックし、証明書を有効にします。

最後に「 ポリシーをアタッチ 」ボタンをクリックします。


f:id:iAtom:20201216173156j:plain


最初に作成した「 ポリシー 」を選択し、「 モノの登録 」ボタンをクリックします。


f:id:iAtom:20201216173116j:plain


以上で IoT CoreにIoT Deviceの登録が完了しました。 次はIoT Device(Raspberry Pi)で動作する、IoT Device Shadowのプログラムです。

ルートCAファイルのダウンロード

Raspberry PiSSH接続してログインします。

  $ ssh pi@192.168.10.10


保存するディレクトに移動し、ルートCAファイルダウンロードします。

   $ cd  /home/pi/Documents/testShadow/cert
   $ sudo wget -O root.ca.pem https://www.amazontrust.com/repository/AmazonRootCA1.pem


Raspberry Pi環境

certディレクトリ配下にはルートCA、証明書、キー情報ファイルを配置しますので、 本記事箇所でダウンロードした証明書、キー情報ファイルはコピーしてください。

imageディレクトリは、カメラ起動中にキャプチャした画像を保存する場所です。

 testShadow/
       |
       |--- shadow_camera_test.py 
       |
       |--- cert/
       |           |--- root.ca.pem                      #  ルートファイル
       |           |--- xxxxxxxxxxx.cert.pem"      #証明書ファイル
       |           |--- xxxxxxxxxxx.private.key"   #privateキーファイル
       |
       |--- image/

ソースコード

OpenCV」、「AWSIoTPythonSDK.MQTTLib」はインストールしておいてください。

ソースコードは、「AWSIoTPythonSDK」の「sample」ディレクトリにあるソースファイルを参考にしました。

AWSIoTPythonSDK

github.com


updateState()、customShadowCallback_Delta()、54行目〜77行目も一部変更していますが、ほぼ流用です。

カメラ起動中は1秒周期でキャプチャーした画像を保存します。

ソースコード内の「 ■ 」コメントがある箇所は自分の環境に合わせて修正してください。


from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTShadowClient
import logging
import time
import json
import argparse
import cv2
import datetime

# VideoCapture用インスタンス生成 
cap = cv2.VideoCapture(0)
# バッファサイズ1に変更
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)

# カメラ状態保存処理
def cameraStatusSet(onoff):
    global cameraState
    # カメラ起動指示有無確認
    if onoff == "1" :
        cameraState = 'on'
    else:
        cameraState = 'off'

# IoT Device Shadow "reported" UpDate処理
def updateState(newState):
    JSONPayload = '{"state":{"reported":{"camera":' + str(newState) +'}}}'
    print("updating...\n")
    print(JSONPayload)
    deviceShadowHandler.shadowUpdate(JSONPayload, None, 5)
    cameraStatusSet(newState)

# Custom Shadow callback
def customShadowCallback_Delta(payload, responseStatus, token):
    print(responseStatus)
    payloadDict = json.loads(payload)
    print("++++++++DELTA++++++++++")
    print("camera: " + str(payloadDict["state"]["camera"]))
    print("version: " + str(payloadDict["version"]))
    print("+++++++++++++++++++++++\n\n")

    # reported Update
    newState = json.dumps(payloadDict["state"]["camera"])
    updateState(newState)


host = "xxxxxxxxxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com"   # ■自環境に合わせて設定
port = 8883  # MQTT
clientId = "RaspberryPi"   # ■ ClientidはthingNameと同じにするとIoT Coreの
                                       # ■ shadow変換が見えなくなるので、違う名前にした
thingName = "RaspberryPi_Camera"# ■AWS IoT Coreで「モノの作成」で生成した名前
rootCAPath = "./cert/root.ca.pem"
certificatePath = "./cert/xxxxxxxxxxxx-certificate.pem.crt" # ■ 自環境に合わせて設定
privateKeyPath = "./cert/xxxxxxxxxxxx-private.pem.key"       # ■ 自環境に合わせて設定

# Configure logging
logger = logging.getLogger("AWSIoTPythonSDK.core")
logger.setLevel(logging.DEBUG)
streamHandler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
streamHandler.setFormatter(formatter)
logger.addHandler(streamHandler)

# Init AWSIoTMQTTShadowClient
myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient(clientId)
myAWSIoTMQTTShadowClient.configureEndpoint(host, port)
myAWSIoTMQTTShadowClient.configureCredentials(rootCAPath, privateKeyPath, certificatePath)

# AWSIoTMQTTShadowClient configuration
myAWSIoTMQTTShadowClient.configureAutoReconnectBackoffTime(1, 32, 20)
myAWSIoTMQTTShadowClient.configureConnectDisconnectTimeout(10)  # 10 sec
myAWSIoTMQTTShadowClient.configureMQTTOperationTimeout(5)  # 5 sec

# Connect to AWS IoT
myAWSIoTMQTTShadowClient.connect()
# Create a deviceShadow with persistent subscription
deviceShadowHandler = myAWSIoTMQTTShadowClient.createShadowHandlerWithName(thingName, True)
# Listen on deltas
deviceShadowHandler.shadowRegisterDeltaCallback(customShadowCallback_Delta)

# カメラON/OFF状態を初期化
cameraState = 'off'

# Loop forever
while True:
    # カメラOPEN状態
    while(cap.isOpened()):
        # カメラ起動状態指示 ?
        if cameraState == 'on':
            # カメラで顔データを取得する
            ret, img = cap.read()
            # 日時取得
            dt_now = datetime.datetime.now()
            dateStr = dt_now.strftime('%Y%m%d_%H%M%S_%f')
            dateStr1 = dt_now.strftime('%Y/%m/%d %H:%M:%S')
            # 画像内に日時を書き込み
            cv2.putText(img, dateStr1 ,(20,450), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)
            # 画像ファイル保存
            cv2.imwrite('./image/image_'+ dateStr + '.jpg',img)
        time.sleep(1)

IoT Device shadow起動

    $ python3 shadow_camera_test.py


起動すると、標準出力で表示ログを出力します。

下記の表示イメージは、全て「AWSIoTPythonSDK.MQTTLib」ライブラリで出力しています。

  2020-12-17 10:01:55,642 - AWSIoTPythonSDK.core.protocol.internal.clients - DEBUG - Initializing MQTT layer...
  2020-12-17 10:01:55,644 - AWSIoTPythonSDK.core.protocol.internal.clients - DEBUG - Registering internal event callbacks to MQTT layer...
  2020-12-17 10:01:55,644 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - MqttCore initialized
  2020-12-17 10:01:55,645 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Client id: RaspberryPi
  2020-12-17 10:01:55,645 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Protocol version: MQTTv3.1.1
  2020-12-17 10:01:55,645 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Authentication type: TLSv1.2 certificate based Mutual Auth.
  2020-12-17 10:01:55,645 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Configuring offline requests queueing: max queue size: 0
  2020-12-17 10:01:55,646 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Configuring offline requests queue draining interval: 0.100000 sec
  2020-12-17 10:01:55,646 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Configuring endpoint...
  2020-12-17 10:01:55,647 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Configuring certificates...
  2020-12-17 10:01:55,647 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Configuring reconnect back off timing...
  2020-12-17 10:01:55,648 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Base quiet time: 1.000000 sec
  2020-12-17 10:01:55,648 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Max quiet time: 32.000000 sec
  2020-12-17 10:01:55,648 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Stable connection time: 20.000000 sec
  2020-12-17 10:01:55,649 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Configuring connect/disconnect time out: 10.000000 sec
  2020-12-17 10:01:55,649 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Configuring MQTT operation time out: 5.000000 sec
  2020-12-17 10:01:55,649 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Performing sync connect...
  2020-12-17 10:01:55,649 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Performing async connect...
  2020-12-17 10:01:55,649 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Keep-alive: 600.000000 sec
  2020-12-17 10:01:55,650 - AWSIoTPythonSDK.core.protocol.internal.workers - DEBUG - Event consuming thread started
  2020-12-17 10:01:55,651 - AWSIoTPythonSDK.core.protocol.mqtt_core - DEBUG - Passing in general notification callbacks to internal client...
  2020-12-17 10:01:55,651 - AWSIoTPythonSDK.core.protocol.internal.clients - DEBUG - Filling in fixed event callbacks: CONNACK,DISCONNECT,MESSAGE
  2020-12-17 10:01:55,774 - AWSIoTPythonSDK.core.protocol.internal.clients - DEBUG - Starting network I/O thread...
  2020-12-17 10:01:55,848 - AWSIoTPythonSDK.core.protocol.internal.workers - DEBUG - Produced [connack] event
  2020-12-17 10:01:55,849 - AWSIoTPythonSDK.core.protocol.internal.workers - DEBUG - Dispatching [connack] event
  2020-12-17 10:01:55,850 - AWSIoTPythonSDK.core.protocol.internal.workers - DEBUG - No need for recovery
  2020-12-17 10:01:55,851 - AWSIoTPythonSDK.core.protocol.internal.clients - DEBUG - Invoking custom event callback...
  2020-12-17 10:01:55,852 - AWSIoTPythonSDK.core.protocol.mqtt_core - INFO - Performing sync subscribe...
  2020-12-17 10:01:55,852 - AWSIoTPythonSDK.core.protocol.internal.workers - DEBUG - Adding a new subscription record:$aws/things/RaspberryPi_Camera/shadow/update/delta qos: 0
  2020-12-17 10:01:55,853 - AWSIoTPythonSDK.core.protocol.internal.clients - DEBUG - Filling in custom suback event callback...
  2020-12-17 10:01:55,907 - AWSIoTPythonSDK.core.protocol.internal.workers - DEBUG - Produced [suback] event
  2020-12-17 10:01:55,908 - AWSIoTPythonSDK.core.protocol.internal.workers - DEBUG - Dispatching [suback] event
  2020-12-17 10:01:55,908 - AWSIoTPythonSDK.core.protocol.internal.clients - DEBUG - Invoking custom event callback...
  2020-12-17 10:01:55,909 - AWSIoTPythonSDK.core.protocol.internal.clients - DEBUG - This custom event callback is for pub/sub/unsub, removing itafter invocation...
  2020-12-17 10:01:57,912 - AWSIoTPythonSDK.core.shadow.deviceShadow - INFO - Subscribed to delta topic for deviceShadow:RaspberryPi_Camera
  2020-12-17 10:02:01,042 - AWSIoTPythonSDK.core.protocol.internal.workers - DEBUG - Produced [message] event
  2020-12-17 10:02:01,042 - AWSIoTPythonSDK.core.protocol.internal.workers - DEBUG - Dispatching [message] eventdelta/RaspberryPi_Camera


参考までに、shadow差分を検知すると以下の画面ログが出力されます。


  ++++++++DELTA++++++++++
  camera: 1
  version: 12
  +++++++++++++++++++++++

updating...


AWSとIoT Deviceの設定が全て終わりました。

遠隔から指示する場合、iphoneだけではなくPCからも制御可能です。 コマンドイメージはPart4の方で。

AWS IoT Device Shadowを使ってみた!(Part2)

f:id:iAtom:20201218174122j:plain

本記事は、最終目標である遠隔からDevice Shadow機能を使ってIoT Device制御をまとめたものです。 なお、4つの記事の中で今回は 下記の「 Part2 」です

主な全体記事の流れです。

■ Part1 : API Gatewayから起動するLambda関数の作成

■ Part2:API GatewayAPI作成

■ Part3 : AWS IoT Deviceとshadow作成

■ Part4 : iPhoneアプリ作成 と 遠隔Camera起動確認

AWS構成図

赤点線で囲んだ箇所を今回作成します。


f:id:iAtom:20201217154405j:plain

API の作成

AWS API Gatewayコンソールにいきます。


f:id:iAtom:20201208084054j:plain


APIを作成 」ボタンをクリックします。


f:id:iAtom:20201216141755j:plain


APIタイプを「 REST API」の「 構築 」ボタンをクリックします。


f:id:iAtom:20201216141955j:plain


API」入力、「 エンドポイントタイプ」を選択します。ここではAPI名は「cameraAPITest」、「リージョン」を指定し、「 APIの作成 」ボタンをクリックします。

エンドポイントにリージョンを選択すると、外部から指定するURLに「リージョン名」を付与しているアドレスになるイメージです。

下記のリージョン名は、「ap-northeast-1」です。尚、リージョン名は「メソッド作成」箇所で選択します。 (https:xxxxxxxxxxxxxxxxxxxx.ap-northeast-1.amazonaws.com/xxxxxxxxxxxxxx)


f:id:iAtom:20201216142316j:plain


リソースの作成

アクション 」ボタンをクリックし「 リソースの作成 」を選択します。


f:id:iAtom:20201208084930j:plain


リソース名 」を入力し、「 リソースの作成 」をクリックし、リソースを作成します。 「リソース名」は「リソースパス」欄に自動挿入されます。

「リソースパス」とは、外部から指定するURL(https:................/test )のアドレスとして扱われます。ここでは「リソース名」を「test」としているため、パスにtestが入ります。


f:id:iAtom:20201208085700j:plain


メソッドの作成

アクションボタン 」をクリックし、「 メソッドの作成 」を選択します。

「メソッド」とは、HTTPメソッドのことです。 HTTPメソッドとは、リソースに対して実行したいアクションを示すもので、メソッドには「GET」、「POST」、「PUT」などがあり、HTTPサーバー(今回はAWSがサーバー)へ指定する時に使用します。

AWS API Gatewayは、一つのメソッドに一つのLambda functionを登録できますので、メソッド毎にLambda functionを登録し、別の処理を行うことができます。


f:id:iAtom:20201208090002j:plain


プルダウンリストからHTTPリクエストメソッド「 POST 」を選択します。選択完了後は、「 チェックマーク 」をクリックします。


f:id:iAtom:20201208093105j:plain


Lambda関数の登録

左側のツリー表示から「POST」を選択し、POSTのセットアップをします。 統合タイプは「 Lambda関数 」のラジオボタンで選択、「 Lambdaリージョン 」を選択します。最後に「 Lambda関数 」に関数名を入力&選択し、「 保存 」ボタンをクリックします。

Lambda関数名は、「Part1」で作成した「」です。キーボードで登録したい関数名を入力すると、作成済みのLambda関数が表示されますので、対象の関数があったらそれを選択しましょう。

f:id:iAtom:20201216143103j:plain

Lambda関数に権限を追加する 」のダイアログが表示されます。「 OK 」ボタンをクリックします。


f:id:iAtom:20201216143322j:plain


APIキーの作成

ここからが「 外部からアクセスするためにURLを公開」する登録です。

ただし、APIをデプロイした時点で外部に公開され、誰でもアクセスできてしまいます。 Webブラウザからアクセスするブログなどのサイトではないので、誰からもアクセスできないようAPIキーを使用して認証を行うようにします。

先にAPIキーを作成し、APIリクエスト時に認証を行うようにします。

左メニューから「 APIキー 」を選択。「 アクションボタン 」をクリックし、「 APIキーの作成 」を選択します。


f:id:iAtom:20201216143832j:plain


名前 」を入力し、「 APIキー 」は自動生成とし、「 保存ボタン 」をクリックします。


f:id:iAtom:20201216144029j:plain


作成が完了すると、APIキーが生成されます。このAPIキーは、外部からアクセスするときに使用します。「 表示 」をクリックでキー情報を確認できます。


f:id:iAtom:20201216144313j:plain


APIキーをメソッドと関連付けさせます。左ツリー表示の「 POST 」を選択し、右画面の「 メソッドリクエス 」をクリックします。


f:id:iAtom:20201216145224j:plain

APIキーの必要性 」が「 false 」になっていますので、プルダウンで「 true 」を選択し、「 チェックボタン 」をクリックします。


f:id:iAtom:20201208100601j:plain


マッピングプレートの作成

URLでパラメータ取得のための「 マッピングプレート 」の登録をします。

POSTメソッドの「 統合リクエス」をクリックします。


f:id:iAtom:20201216154314j:plain


マッピングプレート 」をクリックします。


f:id:iAtom:20201216154423j:plain


テンプレートが定義されていない場合(推奨)」を選択し、Content-Typeに「 application/json 」を入力し、チェックボタンをクリックします。


f:id:iAtom:20201216154638j:plain


チェックボタンクリック後、画面下に「 設定欄 」が表示されますのでイメージのように入力し「 保存」ボタンをクリックし保存します。


f:id:iAtom:20201216154848j:plain

設定欄の入力イメージです。

  {
   "camera": "$input.params('camera')"
  }


これでURLでパラメータ取得の設定が終わりました。

APIのデプロイ

アクションボタン 」をクリックし、「 APIのデプロイ 」を選択します。


f:id:iAtom:20201208101054j:plain

デプロイ後に各種設定変更した場合は、同様に再デプロイすることで設定が反映されます。


APIのステージを登録します。

このステージは、実際の使用する時エンドポイントを分けて使用することができます。例えば開発用、評価が終わって正式公開用などのようにもできます。

今回はお試しなので「 デプロイされるステージ 」を「新しいステージ」、「 ステージ名 」を「development」とし、「 デプロイ 」ボタンをクリックし、デプロイします。

このステージ名がURL(https:xxxxxxxxxxxxx/development/test)に割り当てられます。


f:id:iAtom:20201208101927j:plain


デプロイ 」が完了するとステージまでのURLアドレスが表示されます。これに「 リソース名」(今回はtest)が追加されたアドレスがアクセスアドレスになります。


f:id:iAtom:20201208102631j:plain


使用量プランの作成

使用量プランを設定します。作成したAPIの使用量を測定することに使用されます。 左メニュー画面から「 使用プラン 」を選択し、「 作成 」ボタンをクリックします。


f:id:iAtom:20201208103510j:plain


プラン「 名前 」を入力し、「ロットリングの有効化 」「 クォータを有効する 」のチェックボックスのチェックを外して、「 次へ 」のボタンをクリックします。


f:id:iAtom:20201216150307j:plain


APIステージと使用プランを関連付けます。下記画像の「 APIステージの追加 」ボタンが非Active化(グレー表示)なっていますが、実際はボタンはActive化状態となっていますので、ご了承ください。

APIステージの追加 」ボタンをクリックし、「 API 」、「 ステージ 」を選択し、「 チェックマーク 」をクリックします。


f:id:iAtom:20201216150557j:plain


完了すると、「 次へ 」ボタンがActive化されますので、ボタンをクリックします。


f:id:iAtom:20201216150723j:plain

APIキーと使用プランを関連付けします。下記画像の「 APIキーを使用プランに追加 」ボタンが非Active化(グレー表示)なっていますが、実際はボタンはActive化状態となっていますので、ご了承ください。

名前のテキストボッククス 」に、APIキー名を入力すると選択表示が出ますので選択します。


f:id:iAtom:20201216151214j:plain


チェックマーク 」をクリックします。


f:id:iAtom:20201216151236j:plain


完了すると、「 完了 」ボタンがActive化されますので、ボタンをクリックします。


f:id:iAtom:20201216151342j:plain

これで外部からAPIキーを使用したURLアクセスができるようになりました。

次はPart3のAWS IoT Deviceとshadow作成です。