🫥

M5Stackを使って、透明ディスプレイにデータを出力してみた

2024/02/23に公開

透明ディスプレイって何か憧れますよね?

ということで、ロマンだけを追い求めて、透明ディスプレイを買ってみました。

利用デバイス

今回利用するデバイスは以下の通りです。

M5Stack Core2 for AWS

https://www.switch-science.com/products/6784

M5Stack Core2 for AWSを利用したチュートリアルが用意されており、MQTTプロトコルを利用した通信や、Amazon SageMakerを利用した機械学習に関することも少しだけ学べます。
https://aws-iot-kit-docs.m5stack.com/ja/getting-started/

1.5インチ透明OLEDディスプレイユニット

https://www.switch-science.com/products/8866

※後述するライブラリのマニュアルを眺めていたところ、
後継機が発売されていることを知りました🥺

https://www.switch-science.com/products/9273

開発環境

PlatformIO

Visual Studio Codeから、 PlatformIO IDEをインストールします。
https://marketplace.visualstudio.com/items?itemName=platformio.platformio-ide

構成図

今回の記事で紹介するシステムの大まかな構成図です。
M5Stack送信したデータを基に、Lambdaを実行するやつです。

構築手順

AWS IoT

  1. IoT Coreサービスの画面へ移動します。

  1. 管理>すべてのデバイス>モノ をクリックします。
    (´-`).。oO(英語ではThingですが、日本ではモノなのは直訳すぎるような・・・?)

  1. 「モノを作成」ボタンをクリックします。(ここでは「M5Stack」と設定しています。)
  2. 「1つのモノを作成」を選択して、次へボタンをクリックします。
  3. モノの名前を入力し、次へボタンをクリックします。
  4. 「新しい証明書を自動生成 (推奨)」を選択して、次へボタンをクリックします。
  5. モノ用のポリシー設定を作成し、アタッチします。
  6. 「モノを作成」ボタンをクリックします。
  7. 最後に証明書ファイルをダウンロードして完了です。

ポリシー設定例
ポリシー設定例
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:Connect",
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Publish",
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Subscribe",
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Receive",
      "Resource": "*"
    }
  ]
}

AWS Lambda

M5Stackへデータを送信するための、Lambdaを作成します。

lambda_function.py
import boto3
import json
from datetime import datetime as dt

client = boto3.client('iot-data')

def lambda_handler(event, context):

    now_datetime = dt.now()
    now_datetime_string = now_datetime.strftime('%Y/%m/%d %H:%M:%S')
    topic = "M5Stack/sub"
    
    datum = json.dumps({"message": now_datetime_string})

    response = client.publish(
        topic=topic,
        qos=0,
        payload=datum,
        retain=True
    )

LambdaからAWS IoTへアクセスするために、ロールをアタッチしておきます。

AWS IoT ルールの作成

AWS IoTにて、MQTT受信後にLambdaを実行させるために、ルールを追加します。

  1. メッセージのルーティング>ルールをクリックします。

  2. 「ルールの作成」ボタンをクリックします。

  3. ルール名を設定し、「次へ」ボタンをクリックします。

  4. SQLステートメントについて以下の値をセットし、「次へ」ボタンをクリックします。({モノの名前}/pub

SELECT * FROM 'M5Stack/pub'
  1. ルールアクションに、先ほど作成したLambda関数を指定し、「次へ」ボタンをクリックします。

  1. 設定内容を確認し、作成ボタンをクリックしたら完了です。

M5Stack

M5Stack側では、一定の間隔でAWS IoTへデータを送信するようにしてみます。

使用したライブラリはM5Unifiedです。
製品ごとに異なっていたライブラリの仕様を共通化してしまった神ライブラリって認識でOKです。
https://github.com/m5stack/M5Unified

PlatformIOでプロジェクトを作成する

  1. VS Codeのサイドメニューから蟻の顔っぽいアイコンをクリックします。

  2. Platform IOのフォーム画面から「New Project」ボタンをクリックし、プロジェクト名、Board(利用するデバイス)を設定して「Finish」ボタンをクリックします。

  3. 各種データのダウンロードが完了するまでしばらく待ちます。

  4. プロジェクトファイルを参照できるようになったら、Platform IOのHome画面にある「Libraries」メニューを選択します。

  5. 検索テキストボックスに「M5Unified」と入力し、検索します。

  6. 検索結果の中から「M5Unified」をクリックし、「Add to Project」ボタンをクリックします。

  1. 「Select a project」のプルダウンメニュー内から、先ほど作成したプロジェクトを指定し、「Add」ボタンをクリックして完了です。

ソースコード

コードについては、この記事を参考にさせていただきました🙏
https://dev.classmethod.jp/articles/m5stack-core2-for-aws-arduino-mqtt/

処理については以下のことを行っています。

  • 透明ディスプレイへの接続
  • WiFiへの接続
  • AWS IoTへの接続
  • AWS IoTへのデータ送信
  • AWS IoTからのデータ受信
  • 透明ディスプレイへの受信データ出力
main.cpp
#include <M5ModuleDisplay.h>
#include <M5UnitGLASS.h>
#include <M5Unified.h>

// LED
#include <Adafruit_NeoPixel.h>
#define PIN 25
#define NUMPIXELS 9
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

// AWS IoT
#include <ArduinoJson.h>
#include <WiFiClient.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#define WIFI_SSID "{WIFI_SSID}"
#define WIFI_PASSPHREASE "{WIFI_PASSWORD}"
#define DEVICE_NAME "{モノの名前}"
#define AWS_IOT_ENDPOINT "{AWS_IOT_ENDPOINT}"
#define AWS_IOT_PORT 8883
#define PUBLISH_TOPIC DEVICE_NAME"/pub"
#define SUBSCRIBE_TOPIC DEVICE_NAME"/sub"
#define QOS 0

// certs.cpp
extern const char *root_ca;
extern const char *certificate;
extern const char *private_key;

WiFiClientSecure https_client;
PubSubClient mqtt_client(https_client);

bool blink = false;

void callback(char*, byte* , unsigned int);

// WiFi Connect
void connect_wifi() {
    M5.Lcd.print("WiFi Connecting to ");
    M5.Lcd.println(WIFI_SSID);
    WiFi.begin(WIFI_SSID, WIFI_PASSPHREASE);
    while (WiFi.status() != WL_CONNECTED) {
        M5.Lcd.print(".");
        delay(500);
    }
    M5.Lcd.println("Connected");
    M5.Lcd.printf("IPv4: %s", WiFi.localIP().toString().c_str());
    M5.Lcd.println("");
}

// AWS IoT Connect
bool connect_awsiot() {
    https_client.setCACert(root_ca);
    https_client.setCertificate(certificate);
    https_client.setPrivateKey(private_key);
    mqtt_client.setServer(AWS_IOT_ENDPOINT, AWS_IOT_PORT);
    mqtt_client.setCallback(callback);

    while (!mqtt_client.connected())  {
        M5.Lcd.println("MQTT connection...");
        if (mqtt_client.connect(DEVICE_NAME)) {
            M5.Lcd.println("Connected");

            mqtt_client.subscribe(SUBSCRIBE_TOPIC, QOS);
            M5.Lcd.println("Subscribed.");
            delay(1000);

            return true;
        } else {
            M5.Lcd.printf("Failed, rc=%d", mqtt_client.state());
            M5.Lcd.println("");
            return false;
        }
    }
}

void publish() {
    StaticJsonDocument<200> json_document;
    char json_string[100];
    json_document["blink"] = blink;
    serializeJson(json_document, json_string);
    mqtt_client.publish(PUBLISH_TOPIC, json_string);
}

void callback(char* topic, byte* payload, unsigned int length) {
    blink ^=1;
    for(int i=0;i<9;i++) {
        pixels.setPixelColor(i, pixels.Color(0, 0, blink?255:0));
    }
    pixels.show();
    M5.Displays(1).clear();
    M5.Displays(1).setCursor(0, 0);
    M5.Displays(1).print("Message arrived [");
    M5.Displays(1).print(topic);
    M5.Displays(1).print("] ");
    for (int i = 0; i < length; i++) {
        M5.Displays(1).print((char)payload[i]);
    }
}

void setup(void)
{
    auto cfg = M5.config();
    cfg.external_display.module_display = true;
    cfg.external_display.unit_glass     = true;

    M5.begin(cfg);

    int display_count = M5.getDisplayCount();

    int index_module_display = M5.getDisplayIndex(m5::board_t::board_M5ModuleDisplay);
    int index_unit_glass = M5.getDisplayIndex(m5::board_t::board_M5UnitGLASS);

    if (index_module_display >= 0) {
        M5.Displays(index_module_display).print("This is Module Display\n");
    }
    if (index_unit_glass >= 0) {
        M5.Displays(index_unit_glass).print("This is Unit GLASS\n");
    }

    Serial.begin(115200);
    M5.Lcd.setTextFont(4);

    connect_wifi();
    connect_awsiot();
    M5.delay(5000);
}

void loop(void)
{
M5.delay(1);
    M5.Lcd.setCursor(0, 0);
    mqtt_client.loop();
    publish();
    delay(5000);
}

モノの作成時にダウンロードした認証ファイルを参照し、各認証情報のデータを貼り付けます。

cert.cpp
const char *root_ca = R"(-----BEGIN CERTIFICATE-----
......
-----END CERTIFICATE-----
)";

const char *certificate = R"(-----BEGIN CERTIFICATE-----
.....
-----END CERTIFICATE-----
)";

const char *private_key = R"(-----BEGIN RSA PRIVATE KEY-----
.....
-----END RSA PRIVATE KEY-----

)";

M5Stackへ書き込む

  1. Platform IOから「New Terminal」をクリックして、ターミナルウィンドウを表示させます。

  1. 以下のコマンドを実行し、プロジェクトのビルドを行います。
pio run
  1. ビルドに成功したら、PCとM5Stackを接続して、以下のコマンドを実行し、コンパイルしたファームウェアをアップロードします。
pio run --target upload

動作確認

M5Stack本体

WiFiへの接続状況を表示されていることを確認できました。

透明ディスプレイ

Lambdaから受信したデータが表示されていますね👌

https://youtu.be/XDmhpbXPftc

まとめ

初めてM5StackとAWS IoTを触ってみましたが、思ったより早く実装ができました。
他にもモジュールを購入しているので、AWS IoTと組み合わせたシステムを作成していきたいと思います🫡

透明ディスプレイを使ったスカウターとか作れないかなぁ・・・

コラボスタイル Developers

Discussion