メインコンテンツまでスキップ

· 約8分
moritalous
お知らせ

過去にQiitaに投稿した内容のアーカイブです。

AWS Lambdaで動作するJavaは初回が遅いですが、速くする方法がないか調べました。 末尾にある参考サイトの内容にたどり着いて、実際に試してみたのでその記録です。

レイテンシ情報はX-Rayにて取得しました。

テスト対象

S3にファイルをPUTするだけのものです

S3Client s3 = S3Client.builder().region(Region.AP_NORTHEAST_1).build();
PutObjectResponse result = s3.putObject(
PutObjectRequest.builder().bucket(ENV_BUCKET).key("filename.txt").build(),
RequestBody.fromString("contents"));

検証1 普通に実行

まずは普通に試してみます。

ソース全体
package helloworld;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;

import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;

public class TestTarget0429 implements RequestHandler<Object, Object> {

public Object handleRequest(final Object input, final Context context) {
String ENV_BUCKET = System.getenv("BUCKET");

S3Client s3 = S3Client.builder().region(Region.AP_NORTHEAST_1).build();
PutObjectResponse result = s3.putObject(
PutObjectRequest.builder().bucket(ENV_BUCKET).key("filename.txt").build(),
RequestBody.fromString("contents"));

System.out.println(result);

return null;
}
}
回数レイテンシ(ms)処理内容
16200
2422
3217
4210
5315

1回目だけ遅い、いわゆるコールドスタートが遅い状態ですね。 S3に1ファイル作成するだけで6秒は遅いですよねぇ。

検証2 Provisioned Concurrencyを有効化

では昨年末に登場したProvisioned Concurrencyを使うとどうでしょう。 https://aws.amazon.com/jp/blogs/news/new-provisioned-concurrency-for-lambda-functions/

ソースコードは検証1と同じものです。

回数レイテンシ(ms)処理内容
15500
2266
3274
4402
5304

初回が遅いままじゃないか。。 同時実行1をプロビジョンドしただけでも月$14.42かかるのに、あんまりじゃないか。。。

なので、以降はProvisioned Concurrencyを無効にして検証を続けます

検証3 処理の分離(Provisioned Concurrencyなし)

初回に遅い原因を探るため、Lambda初回起動時と2回目起動時で処理を分けてみました。

staticなcount変数を作って、初回呼び出し時のみ速攻returnしてみます。

        if (count == 1) {
count++;
return null;
}
ソース全体
package helloworld;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;

import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;

public class TestTarget0429 implements RequestHandler<Object, Object> {

private static int count = 1;

public Object handleRequest(final Object input, final Context context) {
if (count == 1) {
count++;
return null;
}

String ENV_BUCKET = System.getenv("BUCKET");

S3Client s3 = S3Client.builder().region(Region.AP_NORTHEAST_1).build();
PutObjectResponse result = s3.putObject(
PutObjectRequest.builder().bucket(ENV_BUCKET).key("filename.txt").build(),
RequestBody.fromString("contents"));

System.out.println(result);

return null;
}
}

結果

回数レイテンシ処理内容
1625msInitialization処理のみ
25600msS3 PUT(1回目)
3393msS3 PUT(2回目)
4401msS3 PUT(3回目)
5311msS3 PUT(4回目)

Initialization処理が遅いわけじゃないことがわかりました。 S3 PUT(初回)に時間がかかっているようです。

検証4 初期化処理をstaticにする(Provisioned Concurrencyなし)

S3Clientを作る部分をstatic化してみます。

private static String ENV_BUCKET = System.getenv("BUCKET");
private static S3Client s3 = S3Client.builder().region(Region.AP_NORTHEAST_1).build();
ソース全体
package helloworld;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;

import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;

public class TestTarget0429 implements RequestHandler<Object, Object> {

private static int count = 1;

private static String ENV_BUCKET = System.getenv("BUCKET");
private static S3Client s3 = S3Client.builder().region(Region.AP_NORTHEAST_1).build();

public Object handleRequest(final Object input, final Context context) {
if (count == 1) {
count++;
return null;
}

PutObjectResponse result = s3.putObject(
PutObjectRequest.builder().bucket(ENV_BUCKET).key("filename.txt").build(),
RequestBody.fromString("contents"));

System.out.println(result);

return null;
}
}

結果

回数レイテンシ処理内容
12400msInitialization処理 と S3Clientインスタンスの生成
22200msS3 PUT(1回目)
343msS3 PUT(2回目)
446msS3 PUT(3回目)
578msS3 PUT(4回目)

お!少し1回目の処理時間がかかるようになって、2回目が少し早くなりましたね。 3回目以降も早くなってますがこれもなにか影響があるのでしょうか?

検証5 staticイニシャライザで1回やっちゃう(Provisioned Concurrencyなし)

staticで処理をすれば早くなることがわかりました。 一旦staticイニシャライザでダミーファイルを作成してみます。

static{
PutObjectResponse result = s3.putObject(
PutObjectRequest.builder().bucket(ENV_BUCKET).key("dummy.txt").build(),
RequestBody.fromString("contents"));

System.out.println(result);
}
ソース全体
package helloworld;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;

import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;

public class TestTarget0429 implements RequestHandler<Object, Object> {

private static int count = 1;

private static String ENV_BUCKET = System.getenv("BUCKET");
private static S3Client s3 = S3Client.builder().region(Region.AP_NORTHEAST_1).build();

static{
PutObjectResponse result = s3.putObject(
PutObjectRequest.builder().bucket(ENV_BUCKET).key("dummy.txt").build(),
RequestBody.fromString("contents"));

System.out.println(result);
}

public Object handleRequest(final Object input, final Context context) {
if (count == 1) {
count++;
return null;
}

PutObjectResponse result = s3.putObject(
PutObjectRequest.builder().bucket(ENV_BUCKET).key("filename.txt").build(),
RequestBody.fromString("contents"));

System.out.println(result);

return null;
}
}

結果

回数レイテンシ処理内容
14000msInitialization処理 と staticメソッドによるS3 PUT(1回目)ダミーファイル
242msS3 PUT(2回目)
3125msS3 PUT(3回目)
442msS3 PUT(4回目)
544msS3 PUT(5回目)

めでたく2回目以降が速くなりましたよ~!

検証6 検証5+Provisioned Concurrency

検証5で早くなったので、Provisioned Concurrencyも組み合わせたら、1回目から速くなるのか?!

ソースは検証5と同じものです。

回数レイテンシ処理内容
180msInitialization処理
2370msS3 PUT(2回目)※Provisionedの際にstaticイニシャライザで1回実行済みのため
343msS3 PUT(3回目)
472msS3 PUT(4回目)
584msS3 PUT(5回目)

やりましたよ! 期待してたのはこれです。

最終結果

最終形はこうなりました。

  • staticメソッドでダミーファイル作成を一回やっちゃう
  • Provisioned Concurrency有効
ソース全体
package helloworld;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;

import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;

public class TestTarget0429 implements RequestHandler<Object, Object> {

private static String ENV_BUCKET = System.getenv("BUCKET");
private static S3Client s3 = S3Client.builder().region(Region.AP_NORTHEAST_1).build();

static{
PutObjectResponse result = s3.putObject(
PutObjectRequest.builder().bucket(ENV_BUCKET).key("dummy.txt").build(),
RequestBody.fromString("contents"));

System.out.println(result);
}

public Object handleRequest(final Object input, final Context context) {
PutObjectResponse result = s3.putObject(
PutObjectRequest.builder().bucket(ENV_BUCKET).key("filename.txt").build(),
RequestBody.fromString("contents"));

System.out.println(result);

return null;
}
}
回数レイテンシ処理内容
1552msS3 PUT(2回目)※Provisionedの際にstaticイニシャライザで1回実行済みのため
2118msS3 PUT(3回目)
344msS3 PUT(4回目)
486msS3 PUT(5回目)
5146msS3 PUT(6回目)

めでたし、めでたし。

考察

どうも、Javaのクラスローダーは初めてクラスが呼ばれたタイミングでクラスを読み込むようで、クラスの初期ロードに時間がかかるらしいです。 なので、一回読み込んじゃって、クラスをロード済みにしてしまえば次から速いということのようです。

呼ばれたタイミングじゃなくて、はじめに全部クラスをロードしてくれたらいいんですが、そんなことはできないのですかねぇ。

参考サイトはこちらです。

クラスメソッドさんのブログ https://dev.classmethod.jp/articles/report-best-practive-for-java-on-lambda/

re:Invent 2019でのセッション資料 https://d1.awsstatic.com/events/reinvent/2019/REPEAT_1_Best_practices_for_AWS_Lambda_and_Java_SVS403-R1.pdf https://youtu.be/ddg1u5HLwg8

他に見つけたブログ https://pattern-match.com/blog/2020/03/14/springboot2-and-aws-lambda-provisioned-concurrency/

· 約2分
moritalous
お知らせ

過去にQiitaに投稿した内容のアーカイブです。

最近のiPhoneって、ホームボタンないんですってね。 ホームボタンあったら、嬉しいですよね? Android使いなのでよくしらないですが。

ESP32を使って物理ボタンを追加しましょう。

仕組み

ArduionoのライブラリーでBluetoothキーボードにできるものがあったので、これを使います。

ESP32-BLE-Keyboard https://github.com/T-vK/ESP32-BLE-Keyboard

準備するもの

ESP32-DevKitC V4 ブレッドボード タクトスイッチ

こんな感じです。

IMG_20200430_161245.jpg

ソース

タクトスイッチが押されたらcommand + Hを送ります。それだけです。

#include <Arduino.h>
#include <BleKeyboard.h>

BleKeyboard bleKeyboard;

const int switchPin = 32;

int currentState = 0;
int beforeState = 0;

void setup()
{
Serial.begin(9600);
pinMode(switchPin, INPUT);

bleKeyboard.begin();
}

boolean isChange(int current, int before)
{
return (current != before) && (current == 1);
}

void loop()
{
currentState = digitalRead(switchPin);

if (isChange(currentState, beforeState))
{
Serial.println("Change!!!");

if(bleKeyboard.isConnected()) {
bleKeyboard.press(KEY_LEFT_GUI); // Windows key
bleKeyboard.press(0x68); // H key
delay(100);
bleKeyboard.releaseAll();
}
}

beforeState = currentState;

delay(100);
}

使ってみた

手元にあるiPadで試してみました。 Bluetoothの設定にてESP32 BLE Keyboardとペアリングをすれば準備完了。

タクトスイッを押せばホーム画面に戻ります!期待通り!

ただし!物理キーボードが接続されていると判定されるので、テキスト入力が必要なところでソフトウェアキーボードが出ません!!!

どうしても使いたい場合は、下向き三角(下図)を長押しするとソフトウェアキーボードが出てくれます。一度ソフトウェアキーボードが出る状態になると、次からもソフトウェアキーボードが出ます!

コメント 2020-04-30 160915.png

やった! これで、物理ホームボタンの完成です!

· 約6分
moritalous
お知らせ

過去にQiitaに投稿した内容のアーカイブです。

(2022/7/3更新)過去に作成していた障害の履歴を確認するサイトが見れなくなっていたので、改めて作成しました。AWSとGCPの障害情報が確認できます。

Cloud Status History

サイトの実現方法の解説は個人ブログをご参照ください。


(2020/6/13更新)後半に記載しているJSONですが、どうも更新にタイムラグがあるようで、状況が[RESOLVED]になってから登録されるようです。


4/20の夜にAWSの東京リージョンでSQSやLambdaに障害があったようです。 AWSも無敵ではありません。

障害があった際にすぐに気づきたいですね。

Service Health Dashboardをチェック

AWSに障害が発生した場合には、こちらのサイトでアナウンスされます。 https://status.aws.amazon.com/

こちらのサイトにはRSSも配信されているので、RSSリーダーでチェックしておくと良いですね。 しかし、、、

サービスごとにチェックするRSSファイルが分かれている!!! 「Tokyo」で検索しても120件。。。全件手動で登録するのは無理ですね。 (Slackに通知を行うとか今どきのものを考えたのですが、手動で120件も登録できません。。。)

全RSSファイルをまとめたRSSファイルを作る

Lambdaで定期的にスクレイピングする作戦です。やってやれんことはない。 できたファイルはS3にでも格納して外から見れるようにしましょう。 この例ではService Health Dashboardのタブを一つ指定して処理をするようにしました。 Asia Pacificタブは数が多く、1回に5分ほど時間がかかります。

import os
import requests
from bs4 import BeautifulSoup

base_url = 'https://status.aws.amazon.com'

rss_template = ('<?xml version="1.0" encoding="UTF-8"?>'
'<rss version="2.0">'
' <channel>'
' <title><![CDATA[AWS Service Status]]></title>'
' <link>http://status.aws.amazon.com/</link>'
' <description><![CDATA[AWS Service Status]]></description>'
' </channel>'
'</rss>'
)

def get_rss_list(block):
print('start get_rss_list')
res = requests.get(base_url)
aws_soup = BeautifulSoup(res.text, 'lxml')

tables = aws_soup.find(id=block).find_all('table')

links = []

for tr in tables[1].find('tbody').find_all('tr'):
tds = tr.find_all('td')
links.append({'service': tds[1].text, 'url': tds[3].find('a').get('href')})

return links

def get_rss_item(rss_url):
print(rss_url)
response = requests.get(rss_url)
return response.text

def add_rss_item(rss_text, rss_path, service_name, output_soup):
rss = BeautifulSoup(rss_text, 'xml')
items = rss.find_all('item')
for item in items:
category = rss.new_tag('category')
category.append(service_name)
item.append(category)
output_soup.find('channel').append(item)

def put_object(rss_string, block):
import boto3
client = boto3.client('s3')
client.put_object(
ACL='public-read',
Body=rss_string.encode('utf-8'),
Bucket=os.getenv('S3_BUCKET'),
Key='aws-status'+block+'.rss',
ContentType='application/rss+xml'
)

def lambda_handler(event, context):
block = event['block']
print(block)

output_soup = BeautifulSoup(rss_template, 'xml')

for rss in get_rss_list(block):
url = base_url + rss['url']
text = get_rss_item(url)
add_rss_item(text, url, rss['service'], output_soup)

put_object(str(output_soup), block)

全リージョンの障害情報が取得できるJSONの存在

ここまで頑張って作ったあとに、全リージョンの障害情報が取得できるJSONファイルがあることを知りました。さすがClassmethodさん。憧れる。

【小ネタ】AWSで過去に発生した障害の履歴を確認する方法 | Developers.IO https://dev.classmethod.jp/articles/service-health-status-history/

https://status.aws.amazon.com/data.json がそのJSONです。RSSとだいたい同じ情報が取得できます。 これをRSSにしてやれば、良さそうですね。 処理時間も30秒以内に終わるので、API GatewayでRSSを配信できます。

import requests
from bs4 import BeautifulSoup
from bs4.element import CData
from datetime import datetime, date, time, timezone, timedelta

rss_template = ('<?xml version="1.0" encoding="UTF-8"?>'
'<rss version="2.0">'
' <channel>'
' <title><![CDATA[AWS Service Status]]></title>'
' <link>http://status.aws.amazon.com/</link>'
' <description><![CDATA[AWS Service Status]]></description>'
' </channel>'
'</rss>'
)

item_template = ('<item>'
' <title></title>'
' <link>http://status.aws.amazon.com/</link>'
' <pubDate></pubDate>'
' <guid isPermaLink="false"></guid>'
' <description></description>'
' <category></category>'
'</item>')

def lambda_handler(event, context):
soup = BeautifulSoup(rss_template, 'xml')

r = requests.get('https://status.aws.amazon.com/data.json')
json = r.json()

JST = timezone(timedelta(hours=+9), 'JST')

for item in json['archive']:
title = item['summary']
pubDate = item['date']
pubDate = datetime.fromtimestamp(int(item['date']),JST).strftime('%a, %d %b %Y %H:%M:%S %Z')
guid = item['service'] + item['date']
description = item['description']
category = item['service_name']

item = BeautifulSoup(item_template, 'xml')
item.title.append(title)
item.pubDate.append(pubDate)
item.guid.append(guid)
item.description.append(CData(description))
item.category.append(category)

soup.find('channel').append(item)

response = {
'statusCode': 200,
'isBase64Encoded': False,
'headers': {'Content-Type': 'text/xml;charset=UTF-8'},
'body': str(soup)
}

return response

Webサイトにもしてみました。

data.jsonを使って履歴を確認するWebサイトにしてみました。 4/20の東京リージョンの障害のあとにもバージニアのEC2、4/22のCloudFrontにも障害があったようです。

https://aws-status-rss.s3-ap-northeast-1.amazonaws.com/index.html

image.png

JSONを取得して整形しているだけですが、data.jsonの取得はCORSに引っかかるため、API GatewayのHTTP APIにてCORSを有効にしたHTTP プロキシ統合を作って回避しました。

image.png

image.png

· 約4分
moritalous
お知らせ

過去にQiitaに投稿した内容のアーカイブです。

2017年に書いたポートの開放することなく、Respberry Piに外からアクセスするの第2弾です。

自宅に設置したRaspberry Piに外出先からアクセスしたいことってありますよね。 でも、そのためだけにルーターのポートを解放するのも、セキュリティが心配。

今回はAWS Systems Manager Session Managerを使う方法をご紹介します。 SSHもVNCもポートの開放も不要ですよ!

AWS Systems Manager Session Managerとは

公式サイトによると

Session Manager はフルマネージド型 AWS Systems Manager 機能であり、インタラクティブなワンクリックブラウザベースのシェルや AWS CLI を介して Amazon EC2 インスタンス、オンプレミスインスタンス、および仮想マシン (VM) を管理できます。

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/session-manager.html

基本的にはEC2へのログインに使うと便利なものですが、実はSystems Managerはオンプレ環境でも使用でき、Session Manager機能もEC2でなくても利用できるのです。

導入手順

以下、手順を追って説明します。

IAMサービスロールを作成する

IAMロールを作成します。

項目内容
名称SSMServiceRole(任意の名前)
ポリシーAmazonSSMManagedInstanceCore(AWS 管理ポリシー)
信頼関係ssm.amazonaws.com

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/sysman-service-role.html

アクティベーションを作成する

Raspberry PiにSSMエージェントをインストールする際に必要なアクティベーションキーを作成します。

マネジメントコンソールのハイブリッドアクティベーションメニューから作成します。

項目内容
インスタンス制限1(任意の数)
IAM ロールSSMServiceRole(上の手順で作ったもの)
デフォルトのインスタンス名Raspberry Pi(任意の名前)

作成したあと、画面上にアクティベーションコードアクティベーションIDが表示されるのでメモしておきます。

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/sysman-managed-instance-activation.html

SSMエージェントをインストールする

ここはRaspberry Pi上での作業となります。

mkdir /tmp/ssm
sudo curl https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/debian_arm/amazon-ssm-agent.deb -o /tmp/ssm/amazon-ssm-agent.deb
sudo dpkg -i /tmp/ssm/amazon-ssm-agent.deb
sudo service amazon-ssm-agent stop
sudo amazon-ssm-agent -register -code "[activation-code]" -id "[activation-id]" -region "ap-northeast-1"
sudo service amazon-ssm-agent restart

[activation-code][activation-id]は前の手順でメモしたものを使用します。

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/sysman-install-managed-linux.html

これで設定は完了です。 設置が完了すると、Session ManagerのマネージドインスタンスのところにRaspberry Piが追加されます。

image.png

接続手順

Session Managerのマネージドインスタンスのところでインスタンスを選択し、アクション -> Start Sessionを選択すると、ブラウザ上でターミナルの画面が表示されます。

これ、ブラウザの画面です。すごいですね。su - piとすることでpiユーザーへの切り替えもできます。

image.png

これでいつでもどこでもRaspberry Piと一緒です。 SSHもVNCも不要です。すごいですね。

· 約5分
moritalous
お知らせ

過去にQiitaに投稿した内容のアーカイブです。

とうとうAmazon FreeRTOSにも手を出しました。

環境

デバイス

  • ESP32-DevKitC

開発環境

  • Ubuntu 18.04 (on VirtualBox)

チュートリアルやってみました

基本的にはドキュメントのとおりです。

Getting Started with the Espressif ESP32-DevKitC and the ESP-WROVER-KIT https://docs.aws.amazon.com/freertos/latest/userguide/getting_started_espressif.html

IAMユーザーの準備

手順の途中でIoTのThingを作ったり証明書を作ったりする手順があります。これらの手順は便利ツールが用意されているのでかんたんにできますが、便利ツールの実行に内部ではAWS CLIが使われているため、IAMユーザー(アクセスキー、シークレットアクセスキー)が必要です。必要な権限は

  • AmazonFreeRTOSFullAccess
  • AWSIoTFullAccess

開発環境の準備

ツールチェインの導入

Linuxの場合はこちらの手順です。

https://docs.espressif.com/projects/esp-idf/en/v3.3/get-started-cmake/linux-setup.html

sudo apt-get install git wget libncurses-dev flex bison gperf python python-pip python-setuptools python-serial python-cryptography python-future python-pyparsing cmake ninja-build ccache
mkdir -p ~/esp
cd ~/esp
tar -xzf ~/Downloads/xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz

パスの設定をする。~/.profileに以下を追記

export PATH="$HOME/esp/xtensa-esp32-elf/bin:$PATH"
alias get_esp32='export PATH="$HOME/esp/xtensa-esp32-elf/bin:$PATH"'

sudoなしで/dev/ttyUSB0にアクセスする権限を追加

sudo usermod -a -G dialout $USER

CMakeの導入

https://cmake.org/download/ からcmake-3.16.2-Linux-x86_64.shをダウンロードします。 その後インストール。インストール先は~/cmake-3.16.2-Linux-x86_64にしました。

chmod +x cmake-3.16.2-Linux-x86_64.sh
./cmake-3.16.2-Linux-x86_64.sh

パスの設定をする。~/.profileに以下を追記

export PATH="$HOME/cmake-3.16.2-Linux-x86_64/bin/:$PATH"

Amazon FreeRTOSのダウンロード

releaseブランチをclone

git clone https://github.com/aws/amazon-freertos.git --recurse-submodules -b release

AWS CLIのインストール

pipでインストールできるのは知ってましたが、他のインストール方法もドキュメント上あったのご参考までに。

https://docs.aws.amazon.com/cli/latest/userguide/install-cliv1.html

curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip"
unzip awscli-bundle.zip
sudo ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws

aws configureして、アクセスキー、シークレットアクセスキー、リージョンを設定する。

pythonの必要ライブラリーをインストール

pip install tornado nose --user
pip install boto3 --user

<amazon-freertos>/tools/aws_config_quick_start/configure.jsonに設定を書き込む。 afr_source_dirにはデフォルト値が入ってるのでそのまま(相対パス指定)で問題ありませんでした。 その後、以下のコマンド

cd <amazon-freertos>/tools/aws_config_quick_start
python SetupAWS.py setup

ビルドしてフラッシュ

さていよいよビルドしてフラッシュです。

ビルド

cd <amazon-freertos>
cmake -DVENDOR=espressif -DBOARD=esp32_wrover_kit -DCOMPILER=xtensa-esp32 -S . -B build

ドキュメント上、your-build-directoryとありますが、後続のコメントはこれがbuildである前提で書かれてますのでご注意ください。

cd build
make all -j4

消して、書いて、モニターする。

cd <amazon-freertos>
./vendors/espressif/esp-idf/tools/idf.py erase_flash flash monitor -B build

ドキュメント中は-p /dev/ttyUSB1とあって、私の環境では、/dev/ttyUSB0でした。更にこの-pオプションがなかっても検出してくれるようなので、指定なしでも動きました。

めでたしめでたし。

デモをGreengrass接続のものに変更

ここに書いてある方法でできます。 https://docs.aws.amazon.com/freertos/latest/userguide/gg-demo.html

  • AWS IoTのマネジメントコンソール画面で、モノの中にあるFreeRTOSデバイスにアタッチした証明書にアタッチしているポリシーポリシードキュメントに、greengrassへのアクセス許可を追加
  • GreengrassグループのロールにAmazonS3FullAccess AWSIoTFullAccessを追加。
  • FreeRTOSのモノを、Greengrassグループのデバイスに追加。
  • GreengrassグループのサブスクリプションでソースをFreeRTOS、ターゲットをIoT Cloudにする
  • ソースコードの修正
<amazon-freertos>/vendors/espressif/boards/esp32/aws_demos/config_files/aws_demo_config.h
#define CONFIG_MQTT_DEMO_ENABLED
define CONFIG_GREENGRASS_DISCOVERY_DEMO_ENABLED

Greengrass接続のデモを、センサー値収集に変更

鋭意作成中 動作未確認

demos/greengrass_connectivity/aws_greengrass_discovery_demo.c
+ include "esp_adc_cal.h"

-#define ggdDEMO_MQTT_MSG_DISCOVERY "{\"message\":\"Hello #%lu from Amazon FreeRTOS to Greengrass Core.\"}"
+#define ggdDEMO_MQTT_MSG_DISCOVERY "{\"value\":\"%lu\"}"

+ esp_adc_cal_characteristics_t *adc_chars = calloc(1, sizeof(esp_adc_cal_characteristics_t));
+ esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, ESP_ADC_CAL_VAL_DEFAULT_VREF, adc_chars);
+
+ uint32_t voltage;
+ IotLogInfo( "%lu", ( long unsigned int )voltage);
+ esp_adc_cal_get_voltage(ADC1_CHANNEL_6, adc_chars, &voltage);


- xPublishInfo.payloadLength = ( uint32_t ) sprintf( cBuffer, ggdDEMO_MQTT_MSG_DISCOVERY, ( long unsigned int ) ulMessageCounter ); /*lint !e586 sprintf can be used for specific demo. */
+ xPublishInfo.payloadLength = ( uint32_t ) sprintf( cBuffer, ggdDEMO_MQTT_MSG_DISCOVERY, ( long unsigned int ) voltage );