M5StickCからAWS IoT Coreへの温湿度情報のPublish(送信) | そう備忘録

M5StickCからAWS IoT Coreへの温湿度情報のPublish(送信)

M5StickCとAWS IoT Core

Raspberry Pi から Python でAWS(Amazon Web Services)の IoT Core にデータを Publish(送信)する記事は以前に書いた

今回は M5Stack社の M5StickC に接続した温湿度センサー(BME280)の情報をAWS IoT Core にPublish したので記事として残しておく。

尚、AWS IoT Core と Edge端末とは送信(Publish)の他に受信(Subscribe)する事もできるが今回は送信のみのプログラムとしている。

受信(Subscribe)については別記事で書くつもりだ。

またプログラムは Arduino IDE で C++ で作成している。

M5stickCtoBME280

全体構成図

全体構成図は以下の通り。

M5stickCdからAWS IoT Coreで温湿度気圧情報の送信全体構成図
  • M5StickC と 温湿度気圧センサー(BME280)とはGrove互換端子で接続する
  • M5StickC は Wi-Fi 接続で でMQTTプロトコルでAWS IoT Core と接続する
  • AWS IoT Core とは公開鍵、秘密鍵方式で接続する
  • 温度、湿度、気圧、電圧等のデータは JSON 形式(暗号化されている)で約5分間隔で送信する

必要な機器

M5StickC

M5StickC は M5Stack社の製品でチップは ESP32-PICO を搭載している。

  • 4MBフラッシュ+520K RAMメモリ
  • TFT LCD(TFT液晶ディスプレイ)
  • Wi-Fi、Bluetooth
  • リチウムイオンポリマーバッテリー内蔵(80mAh)
  • 電源ボタン+ボタン×2個
  • USB Type-Cコネクター
  • HY2.0-4P(Grove互換コネクター)
  • LED×1個
  • 赤外線送信
  • 拡張コネクター
  • 3軸加速度センサー+3軸ジャイロセンサー(6軸)
  • マイクロフォン内蔵

などの特徴があるが、詳細なスペックは以前の記事を参照して欲しい。

HY2.0-4P(Grove互換コネクター)に温湿度センサー(BME280)を接続した。

2021年11月 追記

M5StickC は下記の M5StickC Plus に置き換わっている。

スイッチサイエンスでは M5StickC(本体のみ)がまだ販売されていた。

温湿度センサー

温湿度及び気圧を測定できる BME280 モジュールを使用した。

M5StickC と BME280 との配線は前述の全体構成図を参照して欲しい。

BME280 モジュールは電圧レギュレータを積んでいるタイプなので M5StickC からの 5V 出力も受け取ることができる。

購入したモジュールはピンとモジュールが別々になっているのではんだ付けをした。

はんだ付けについては以前の記事を参照して欲しい。

Grove汎用ケーブル

M5StickC と BME280モジュールとの接続に使用した。

AWS側の設定

事前にAWS(Amazon Web Services)側で IoT Core 等の設定を行う。

AWSのコンソールにログインをして IoT Core サービスを検索して選択する。

AWSのコンソールから IoT Coreを選択する

モノの作成

IoT Core のメニューから管理ー>モノー>「作成」ボタンをクリックする。

管理、モノ、作成

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

単一のモノを作成する
  • 名前:M5Stick-C-test

タイプは未設定でも良いのだが M5StickC との接続は初めてなので分類の為にタイプを作成しておくことにした(任意)。

「タイプの作成」ボタンをクリックする。

名前を入力後にタイプの作成
  • 名前:M5Stick-C
  • 説明:M5Stick-C
  • タグ:未設定

を入力して下にスクロールする。

モノのタイプを作成する

「モノのタイプを作成」ボタンをクリックする。

モノのタイプを作成

タイプが作成されてモノのタイプで選択されているので下にスクロールする。

モノのタイプが作成されて選択されている

グループは特に作成せずに(任意)下にスクロールして「次へ」ボタンをクリックする。

次へボタンをクリックする。

証明書

モノが作成されたので続いて証明書を作成する。

複数の証明書の作成方法があるが推奨の1-Click証明書作成の「証明書を作成」ボタンをクリックする。

証明書を作成ボタンをクリックする。
  • このモノの証明書(xxxx-certificate.pem.crt)
  • パブリックキー(xxxx-public.pem.key)
  • プライベートキー(xxxx-private.pem.key)

が作成されるのでダウンロードリンクから3つのファイルをダウンロードして適当なフォルダーに保存する(xxxxの部分は毎回こ異なる)。

このモノの証明書とプライベートキーの中身は後程 config.h に記述する為に使用する。

パブリックキーは IoT Core 側に必要なキーなのでプログラム中では使用しないが、一応ダウンロードしておく。

ファイルの保存後は AWS IoT のルートCAダウンロードのリンクをクリックする。

証明書類のダウンロード

新しいタブでページが開くので “Amazon Root CA 1”のリンクをクリックする。

Amazon Root CAのリンクをクリックする

表示される証明書をテキストファイルに貼り付けて任意の名前をつけて(AmazonRootCAxxxx.pem)ローカルディスクに保存する。

このキーの中身は後程 config.h に記述する為に使用する。

Amazon Root CA

先程の画面に戻って「有効化」ボタンをクリックして証明書を有効化する。

証明書を有効化する

この後、ポリシーを作成してアタッチするのだが「ポリシーをアタッチ」ボタンをクリックするとまだアタッチできるポリシーが無いので、先にポリシーを作成する為に「完了」ボタンをクリックする。

完了をクリックする

ポリシーの作成

左のメニューから安全性ー>ポリシーから「作成」ボタンをクリックする。

  • 名前:M5Stick-C-Test-Policy

を入力してアクション欄に iot と入力するとサジェスト機能で選択可能な一覧が表示される。

まずは接続可能な端末を許可する為のポリシー “iot:Connect” を選択する。

名前とIoT-Connectの許可

選択するとリソースARNが自動的に設定されるので replaceWithAClientId の部分を SKSTC* に打ち替えて許可にチェックを入れる。

“*” はワイルドカードなので SKSTC で始まるClient IDのみ、接続を許可する設定になる。

IoT:Connectの設定

続けて Publish(端末側からの送信、IoT Coreにとっては受信)と Subscribe(受信)の設定を行う為に「ステートメントを追加」ボタンをクリックする。

今回のテストプログラムでは M5StickC での受信は行わないがポリシーとしては設定をしておく。

ステートメントの追加

先程と同様の手順で、

  • iot:Connect:client/SKSTC*
  • iot:Publish:topic/dt/TempAndHumi/*
  • iot:Subscribe:topicfilter/cmd/device/*

を設定して、それぞれ許可にチェックをいれて下にスクロールして「作成」ボタンをクリックする。

Publish 及び Subscribe は topic でフィルタリングしている。

topic は MQTT でメッセージを仕分けるためのキーで “/”(スラッシュ)で階層構造を設定する事ができる。

任意の階層構造に出来るのだが、AWSのデザインペーパーのMQTT Telemetry Topic Syntaxにて推奨構成が載っていたので参照して欲しい。

下にスクロールして「作成」ボタンをクリックする。

アクション毎のポリシーの設定

ポリシーが正常に作成されたので “M5Stick-C-Test-Policy” をクリックしてみる。

ポリシーが正常に作成された

作成されたポリシーがテキストで表示される。

XXXXXXXXXXXX部はアカウントID。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:Connect",
      "Resource": "arn:aws:iot:ap-northeast-1:XXXXXXXXXXXX:client/SKSTC*"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Publish",
      "Resource": "arn:aws:iot:ap-northeast-1:XXXXXXXXXXXX:topic/dt/TempAndHumi/*"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Subscribe",
      "Resource": "arn:aws:iot:ap-northeast-1:XXXXXXXXXXXX:topicfilter/cmd/device/*"
    }
  ]
}

アタッチ

証明書に先ほど作成したポリシーをアタッチする。

メニューから安全性ー>証明書を選択して先程作成した証明書をクリックする。

先程の証明書を呼び出して選択する

アクションー>ポリシーのアタッチを選択する。

アクションー>ポリシーのアタッチ

証明書にポリシーをアタッチする。

先程作成したポリシー(M5Stick-C-Test-Policy)をチェックして「アタッチ」ボタンをクリックする。

証明書にポリシーをアタッチする

エンドポイント

プログラム中で使用するエンドポイントを控えておく。

メニューから管理ー>モノで該当のモノを選択後に相互作用で表示されるRest API エンドポイントを後ほど config.h に記述するので控えておく。

2022年3月31日 追記

今は左のメニューで”設定”を選択するとエンドポイントが表示される。

エンドポイントの取得

以上で AWS 側の設定は終了。

料金について

AWS IoT Core は有料のサービスだ。

現時点(2021年6月)の東京リージョンでのおそよの料金だが、

  • 1台の端末
  • 1年間接続をしたまま
  • MQTT または HTTP でのメッセージ
  • 1日1,000件のメッセージ、1年間で365,000件のメッセージ
  • 1メッセージは128 KB以下(追加料金無し)

の場合は1台辺り、

  • 接続料金:0.0504576ドル/年
  • メッセージ料金:0.438ドル/年

で約 0.488ドル/年の計算になる。

尚、LoRaWAN での接続は料金体系が異なる。

またデバイスシャドウやルールエンジン(特定の条件の時に別のサービスのアクションを実行)などは別料金になる。

無料枠もあるので詳しい最新の料金体系は AWS の公式ページを参照して欲しい。

Arduino IDEの設定

プログラムはこちらのサイトを参考にさせて貰った。

Arduino IDE にて事前に必要なライブラリーをインストールする。

PubSubClientのインストール

Arduino IDE を起動してスケッチー>ライブラリをインクルードー>ライブラリを管理を選択する。

ライブラリを管理


検索欄で “PubSubClient” で検索して「インストール」ボタンでインストールする。

バージョン 2.8.0 をインストールした。

PubSubClientをインストールする

ArduinoJsonのインストール

同様の手順で ArduinoJson ライブラリーをインストールする。

バージョン 6.18.0 をインストールした。

ArduinoJsonのインストール

BME280のインストール

同様の手順で Adafruit BME280 Library をインストールする。

バージョン 2.1.4 をインストールした。

BME280ライブラリーのインストール

プログラム

プログラムは、

  • m5stickc-bme280-awsiotcore.ino
  • config.h

の2つのファイルから構成されていて同じフォルダーに保存している。

m5stickc-bme280-awsiotcore.ino

/**
 *  Created on 2021-06-02
 *  
 *  M5Stick-Cに繋いだBME280で温湿度気圧を測定
 *  検出情報を指定した秒数間隔でAWS IoT CoreにPublishする
 * 
 *  @author: Souichirou Kikuchi
 */

#include <M5StickC.h>
#include <WiFiClient.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <esp_sleep.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <ArduinoJson.h>
#include <stdio.h>
#include "config.h"

#define S_PERIOD 300 // Deep Sleepする秒数

Adafruit_BME280 bme;

void setupM5StickC() {
    M5.begin();
    delay(10 * 1000); // 10秒待機
    setCpuFrequencyMhz(80); // CPU周波数80以上にしないと無線は使用できない
    M5.Axp.begin(false,false,false,false,true); // 省電力の為、DCDC3をオフ
    M5.Axp.ScreenBreath(10); // 画面の輝度を下げる
    M5.Lcd.setRotation(1); // LCDの方向を変える
    M5.Lcd.setTextSize(2); // フォントサイズを2にする
    M5.Lcd.setTextColor(WHITE, BLACK); // 文字を白、背景を黒
    M5.Lcd.setCursor(10, 10); // カーソル位置
}

int setupWiFi() { // Wi-Fi接続
    int cnt = 0; // 接続試行回数
    const int MAX_RETRY = 30; // 最大接続待ち秒数

    WiFi.disconnect(true);
    delay(1000);
    WiFi.begin(ssid, password);
    Serial.println("Connecting...");
    while ((WiFi.status() != WL_CONNECTED) and (cnt < MAX_RETRY)){ // 最大1秒✕MAX_RETRY回待機する
        delay(1000);
        cnt++;
    }
    if (cnt >= MAX_RETRY) { // MAX_RETRY秒間接続できなかった
        Serial.println("Connect NG...");
        return 0;
    }
    Serial.println("Connected");
    Serial.println(WiFi.localIP()); // M5Stick-CのIP Address
    return 1;
}

WiFiClientSecure httpsClient;
PubSubClient mqttClient(httpsClient);

void setupClient() { // AWS IoT Coreとの接続認証
    const int port = 8883;

    httpsClient.setCACert(rootCA);
    httpsClient.setCertificate(deviceCert);
    httpsClient.setPrivateKey(privateKey);
    mqttClient.setServer(endpoint, port);
}

int connectToAWSIoT() { // AWS IoT Coreとの接続
    int cnt = 0; // 接続試行回数
    const int MAX_RETRY = 10; // 最大リトライ回数

    while (!mqttClient.connected() and (cnt < MAX_RETRY)) { // MAX_RETRY回数接続をリトライする 
        if (!mqttClient.connect(clientId)) {
            cnt++;
            delay(1000);
        }
    }
    if (cnt >= MAX_RETRY){ // 接続回数オーバー
      return 0;
    } else {
      return 1;
    }
}

void display(float temp, float humi, float press, float vbat) {
    // 温度、湿度、気圧、バッテリーの電圧をディスプレイに表示(検証時のみ表示する)
    M5.Lcd.fillScreen(BLACK); // 一旦黒くクリア
    M5.Lcd.setCursor(10, 10); // カーソル位置
    M5.Lcd.printf("temp: %4.1f'C\r\n", temp);
    M5.Lcd.printf("humi:%4.1f%%\r\n", humi);
    M5.Lcd.printf("press:%4.0fhPa\r\n", press);
    M5.Lcd.printf("vbat: %4.2fV\r\n", vbat);
}

void publish(float temp_p, float humi_p, float press_p, float vbat_p) { // JSON形式を組み立ててPublishする
    const char* pubTopic = "dt/TempAndHumi/1";
    const int capacity = JSON_OBJECT_SIZE(8);

    configTzTime("JST-9", "ntp.nict.jp", "time.google.com", "ntp.jst.mfeed.ad.jp"); // NTP Serverと同期する
    struct tm timeInfo;
    getLocalTime(&timeInfo); // 現在時刻を取得
    char date_time[20];
    sprintf(date_time, "%04d-%02d-%02d %02d:%02d:%02d",
        timeInfo.tm_year + 1900,
        timeInfo.tm_mon + 1,
        timeInfo.tm_mday,
        timeInfo.tm_hour,
        timeInfo.tm_min,
        timeInfo.tm_sec
    );
    StaticJsonDocument<capacity> env_inf; // JSONファイルを組み立てる
    env_inf["clientId"] = clientId; // 端末ID
    env_inf["date_time"] = date_time; // 現在時刻
    env_inf["temperature"] = temp_p; // 温度
    env_inf["Humidity"] = humi_p; // 湿度
    env_inf["pressure"] = press_p; // 気圧
    env_inf["Voltage"] = vbat_p; // 電圧
    char jsonBuffer[512];
    serializeJson(env_inf, jsonBuffer);
    mqttClient.publish(pubTopic, jsonBuffer); // AWS IoT CoreにPublish
}

void setup() { // Deep Sleepからの復帰はsetupが実行される
    float temp; // 温度
    float humi; // 湿度
    float press; // 気圧
    float vbat; // 電圧
    int cnt = 0; // 試行回数
    const int MAX_RETRY = 10; // 最大試行回数

    // sprintf("setup %s","1");
    setupM5StickC(); // M5Stick-C初期設定
    if (setupWiFi()) { // Wi-Fi接続
        Serial.println("Wi-Fi Connected!");
        setupClient(); // AWS IoT Coreとの接続認証
        Wire.begin(); // I2Cの初期化
        while ((!bme.begin(0x76)) and (cnt < MAX_RETRY)) { // BME280の初期化
            M5.Lcd.println("BME280 init failed"); // 初期化失敗
            cnt++;
            delay(500);
        }
        if (cnt < MAX_RETRY) { // BME280の初期化成功
            Serial.println("BME280 initialized");
            temp = bme.readTemperature(); // 温度の取得
            humi = bme.readHumidity(); // 湿度の取得
            press = bme.readPressure()/100; // 気圧の取得(pa→hPaに変換」)
            vbat = (M5.Axp.GetVbatData() * 1.1 / 1000); // バッテリーの電圧
            if (connectToAWSIoT()) { // AWS IoT Coreに接続成功なら
                mqttClient.loop();
                display(temp, humi, press, vbat); // M5Stick-CのLCDに表示
                publish(temp, humi, press, vbat); // AWS IoT CoreにPublish
            }
        }
    }
    delay(10);
    WiFi.disconnect(true); // WiFi切断
    esp_deep_sleep(1000000LL * S_PERIOD); // S_PERIOD秒Deep Sleepする
}

void loop() {
}

config.h

端末毎に異なる設定情報やセキュアな情報を config.h に保存している。

const char* clientId = "SKSTC0001";
const char* ssid = "Your SSID";
const char* password = "Your Wi-Fi Password";
const char* endpoint = "XXXXXXXXX-ats.iot.ap-northeast-1.amazonaws.com";

const char* rootCA = R"(
-----BEGIN CERTIFICATE-----
MIIDQTCCAimg ・・・
    ・
    ・
    ・
-----END CERTIFICATE-----
)";

const char* deviceCert = R"(
-----BEGIN CERTIFICATE-----
MIIDWTCCAkGgA ・・・
    ・
    ・
    ・
-----END CERTIFICATE-----
)";

const char* privateKey = R"(
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQ ・・・
    ・
    ・
    ・
-----END RSA PRIVATE KEY-----
)";

clientId

端末を識別するID

SKSTC0001 を設定していて後半の 0001 の部分は連番で端末毎に変更可能だが先頭の SKSTC の部分はポリシーで設定しているので変更は出来ない。

ssid

Wi-FiのSSIDを設定する

password

Wi-Fiのパスワードを設定する

endpoint

先程控えておいたエンドポイントを設定する

rootCA

先程ダウンロードした AmazonRootCAxxxx.pem の中身を貼り付ける

deviceCert

先程ダウンロードしたこのモノの証明書(xxxx-certificate.pem.crt)の中身を貼り付ける。

privateKey

先程ダウンロードしたプライベートキー(xxxx-private.pem.key)の中身を貼り付ける。

補足説明

簡単にプログラムの補足説明をしておく。

ライブラリーのインクルード

10~19行目で必要なライブラリーをインクルードしている。

S_PERIOD

S_PERIOD で Deep Sleep(待機)する秒数を指定する。

300秒(5分)を指定する事でおよそ5分間隔で温湿度、気圧を測定して AWS IoT Core に Publish(送信)する。

ESP32 の Deep Sleep モードは消費電力を 10µA~150µA まで抑えることが出来るので電池モードで動作させる際には有効だ。

setupM5StickC()

CPUの周波数やスクリーンの輝度、方向、フォントサイズなど M5StickC の初期設定を行っている。

setupWiFi()

config.h の接続情報を元に Wi-Fi に接続している。

接続されるまで 1秒間 × 30回待機して、時間内に接続できなかった時は後続の処理はスキップする。

setupClient()

AWS IoT Core との接続認証情報の設定を行う。

connectToAWSIoT()

AWS IoT Core と接続を行う。

最大リトライ回数分繰り返す。

display()

M5stickC の LCD(ディスプレイ)へ温度、湿度、気圧、電圧を表示する。

もし電池稼働で消費電力を抑えたい時はこの関数は呼ばなくても良い。

publish()

JSON 形式で送信情報を組み立てて AWS IoT Core に Publish(送信)する。

送信前に NTP Server に接続して現在時刻を取得している。

送信している項目は以下の通り。

  • 端末ID
  • 現在時刻
  • 温度
  • 湿度
  • 気圧
  • 電圧

setup()

Deep Sleep 明けにまずこの関数が呼ばれる。

前述の関数達を呼び出して Wi-Fi接続、AWS IoT Coreとの接続認証を行った後、BME280 より温湿度、気圧を取得する。

電圧を取得後、ディスプレイへの表示、AWS IoT Core への Publish(送信)を行った後、S_PERIOD で指定した秒数分だけ Deep Sleep する。

Deep Sleep 前に WiFi.disconnect(true) で Wi-Fi を切断をしているのは「知的好奇心 for IoT」さんの記事を参考にさせて貰った。

loop()

setup中で Deep Sleep しているのでこの関数は呼ばれない。

プログラムのコンパイルと書き込み

パソコンとの接続

パソコンと M5stickC を USB-TypeC ケーブルで接続する。

パソコンとM5stickCとの接続

ツールの設定

Arduino IDE を起動してツールー>ボードー>ESP32 Arduinoー>M5Stick-C を選択する。

尚、Arduino IDE のインストール&初期設定については以前の記事を参照して欲しい。

ツールでボード情報の設定

もし一覧に M5Stick-C が表示されない時はツールー>ボードマネジャーから esp32 をインストールまたは最新版にバージョンアップする。

2021年6月20日時点の最新バージョンは 1.0.6。

ボードマネージャーからesp32を最新版にアップデート

ツールでの設定項目は以下の通り。

ボード

M5Stick-C

Upload Speed

150000

書き込みエラーが発生するようであれば速度を落としてみる

Partition Schema

初期値を選択した。

大きなサイズのプログラムを書き込む時でサイズエラーになってしまう時は NO OTA を選択する。

OTAについては以前の記事を参照して欲しい。

Core Debug Level

なし

プログラムが上手く動作せずデバッグ情報を埋め込みたい時にレベルに別に “エラー” から “verbose” まで指定する。

シリアルポート

M5stickCが接続されているシリアルポート(COMx)を選択する。

Windows環境での COMポートの確認方法は以前の記事を参照して欲しい。

コンパイルと書き込み

スケッチー>マイコンボードに書き込むを選択(または Ctrl+u)するとプログラムをコンパイル後、マイコンボードに書き込まれる。

※コンパイルのみの時は Ctrl+r

コンパイルとマイコンボードへの書き込み

プログラムが正常に書き込まれると “ボードへの書き込みが完了しました。” のメッセージが表示される。

ボードへの書き込むが完了しました。

実行結果の確認

ボードへの書き込み後、M5stickC の電源をオフにしない限りプログラムは自動的に動き始める。

※電源オフはディスプレイを正面時の左側のボタンを長押し。

AWS IoT Core にて正常にメッセージが受信できているかを確認する。

AWSコンソールにログインをして IoT core のサービスに移動後、テストー>MQTTテストクライアントのページで “トピックのフィルター” 欄に “#” を入力して「サブスクライブ」ボタンをクリックする。

※ “#”(シャープ)は topic ではワイルドカードに相当する。

“dt/TempAndHumi/#” の様な指定の仕方も可能だ。

MQTTテストクライアント

サブスクリプション欄に受信したメッセージ(JSON形式)が表示される。

メッセージの受信

尚、M5stickCの電源をオンのままにしておくと AWS IoT Core へメッセージを送信し続けて料金が加算されてしまう。

テスト終了後や何らかの理由でこの端末からの AWS IoT Core へのアクセスを停止したい時は、安全性ー>証明書から該当の証明書右にある “・・・”(3点)をクリックして “無効化” を選択するとステータスが無効になる。

証明書の無効化

終わりに

以上で M5stickC から AWS IoT Core への温湿度、気圧情報の送信のプログラムの記事を終了とする。

最後に

この記事が何処かで誰かの役に立つことを願っている。

尚、当記事中の商品へのリンクはAmazonアソシエイトへのリンクが含まれています。Amazonのアソシエイトとして、当メディアは適格販売により収入を得ていますのでご了承ください。

souichirou

やった事を忘れない為の備忘録 同じような事をやりたい人の参考になればと思ってブログにしてます。 主にレゴ、AWS(Amazon Web Services)、WordPress、Deep Learning、RaspberryPiに関するブログを書いています。 仕事では工場に協働ロボットの導入や中小企業へのAI/IoT導入のアドバイザーをやっています。 2019年7月にJDLA(一般社団法人 日本デイープラーニング協会)Deep Learning for GENERALに合格しました。 質問は記事一番下にあるコメントかメニュー上部の問い合わせからお願いします。

おすすめ

質問やコメントや励ましの言葉などを残す

名前、メール、サイト欄は任意です。
またメールアドレスは公開されません。