ども!ネットワークスペシャリストのまとんです。
ソラコムのボタンを使って、手元のソレノイドを動かしたい!という人のために、SORACOMボタンとESP32を接続する方法を紹介します。
使用するAWSのサービスはAWS IoT 1-clickと、Lambdaと、AWS IoTです。
- ESP32とは?
- 作りたいもの
- 進捗
- 参考にさせていただいた記事
- 動作環境
- Arduino IDEでESP32の開発環境を整える
- ESP32と自宅のWiFiの接続確認
- AWS IoTの証明書を入手する
- AWS IoTの設定
- AWS IoTとMQTT通信するスケッチ
- Lambdaの設定
- 動作結果
- この記事のまとめ
ESP32とは?
Arduinoライクに開発できるマイコンです。Arduinoを使ったことがある人なら、同じように使えるので移行が簡単です。
ESP32は、下記の点でArduinoより優れています。
Arduinoでこのような無線機能を持たせる場合、追加モジュールを実装しないといけないので、めんどくさいです。
(2)Arduino本家より安い
Arduino Unoは3200円、Arduino microでも2700円のところ、ESP32の開発キット(ESP-WROOM-32)は2180円です。
Arduino以上の機能があって、Arduinoより安い。すごいですね。ESP32を使わない理由がほとんどないです。
ESP32のデメリットとしては、Arduinoよりも統合開発環境(IDE)の提供が不十分であり、環境構築に手間がかかることがありました。
しかし、ESP32の開発スピードは非常に早く、今ではArduino IDEでほぼ全ての機能が使えるようです。なので、もはやESP32のデメリットは無くなったと感じています。
この記事では、ESP32の導入から、ESP32をWiFiに繋ぎ、AWS IoTとの通信を経由して、ソラコムのボタンとの接続を実装します。
作りたいもの
「ボタンを押したらソレノイドが動くおもちゃ」を作りたいです。
AWSの練習を兼ねているので、AWSを介した仕組みを実装します。
想定しているアーキテクチャです。
SORACOMのボタン(SORACOM LTE-M Button powered by AWS)を押すと、AWS上でLambdaが走って、AWS IoTにMQTTをPublishします。
ソレノイドを駆動するArduinoは通信機能を持たないので、WiFiモジュールとセットになったESP32で代用し、iPhoneのテザリングでインターネットに繋げます。
ESP32にAWS IoTの証明書を保存しておけば、これでセキュアに通信できるはず。
進捗
この記事では、オレンジ色の枠内を実装します。
青枠の箇所が気になる方は、過去の記事をご覧ください。
2019/2/24・・・ソラコムのボタンでAWS IoT 1-clickを起動する
【JAWS DAYS 2019参加レポ】「あのボタン」SORACOMの薄い本を買ってみた! - メタラーまとんがハイソにやらかすようです
2019/2/25・・・Arduinoでソレノイドを駆動する
ソレノイド(ZHO-0420S-05A4.5)をArduinoとMOSFETで動かしてみた! - メタラーまとんがハイソにやらかすようです
2019/3/11・・・Windows上でUbuntuを走らせて、AWS IoTと疎通確認する
AWS IoTをWSL(Windows Subsystem for Linux)のUbuntuで動かしてみた! - メタラーまとんがハイソにやらかすようです
2019/4/32・・・ソラコムのボタンでLambdaを起動して、AWS IoTと疎通確認する
参考にさせていただいた記事
Arduino core for the ESP32 のインストール方法 | mgo-tec電子工作
ESP32 Core Board V2 (ESP32 Dev Module)をArduino IDEで使ってみた - Qiita
ESP32〜AWS IoTでMQTT通信して詰んだ話【ClientID】 - Qiita
動作環境
・ESP-WROOM-32 DevKitC V4
waves ESP32 DevKitC V4 ESP-WROOM-32 ESP-32 WiFi BLE 技適取得済 国内発送
- 出版社/メーカー: Espressif Systems
- メディア: エレクトロニクス
- この商品を含むブログを見る
・Windows 10
・Arduino core for the ESP32 1.0.2
・PubSubClient 2.7.0
・ArduinoJson 6.10.1
Arduino IDEでESP32の開発環境を整える
ESP32の開発では、「コーディング→コンパイル→書き込み」の流れを毎回行います。
これらを、自分の好きなエディタでコーディングし、コマンドラインからコンパイルしてもよいですが、
僕のオススメは、全てArduino IDEでやってしまうことです。
コーディングして、ボタン一個で書き込みして、シリアルモニタも見れるのは、やっぱりお手軽です。
最新版のArduino IDEをインストールする
下記の公式サイトから最新バージョンをダウンロードします。この記事では1.8.9でした。
Windowsの場合、オススメは「Windows Installer, for Windows XP and up」です。
最近では、Windows10のアプリ版(Windows app)もリリースされているようですが、僕はまだ使ったことがありません。
ボードマネージャからESP32のボードドライバをインストールする
基本的にはこのサイト様を参考にさせていただきました。
Arduino core for the ESP32 のインストール方法 | mgo-tec電子工作
2018年以前の記事では、「GitHubからzipをダウンロードしてきて~」などの説明が散見されますが、現在ではArduino IDE上でドライバのインストールが完結してしまいます。
ESP32の開発スピードの速さに驚かされます。
では、具体的な手順を説明します。
(1) ESP32のドライバ(Arduino core for the ESP32esp)を提供している下記GitHubにアクセス
GitHub - espressif/arduino-esp32: Arduino core for the ESP32
(2) 「Using Arduino IDE Boards Manager (preferred)」のリンク先を開く
(3) 「Enter https://dl.espressif.com/dl/package_esp32_index.json
into Additional Board Manager URLs field. You can add multiple URLs, separating them with commas.
(4) Arduino IDEを開き、ファイル->環境設定を開く
(5) 「追加のボードマネージャのURL」の右にあるアイコンをクリックして開き、(3)でコピーしたURLをコピーしてOK
(6) ツール->ボード->ボードマネージャを開く
(7) 「esp32」で検索し、「esp32 by Espressif Systems」をインストール。この記事では1.0.2が最新版でした。
(8) USBケーブルでPCとESP32を接続。ツール->ボードでESP32が認識されていればOK。(シリアルポートを選ぶ必要があるかもしれません。今回はCOM6でした)
ボードには様々なESP32開発キットの名前がありますが、今回使ったキット(ESP-WROOM-32 DevKitC V4)では「ESP32 Dev Module」で問題ありませんでした。
ここで、CPU周波数で、ESP32の最大の240MHzを選択できます。
2018年以前の記事では、「Arduino IDEではESP32の性能をフルに使えない」といった記述が散見されていますが、この実装をした2019年4月時点では、全ての機能をフル活用できるように感じました。
各種必要なライブラリをインストールする
EPS32とAWS IoTをMQTTで通信し、jsonをやり取りするためのライブラリをインストールします。
(1) スケッチ->ライブラリをインクルード->ライブラリを管理
(2) 「pubsubclient」で検索し、「PubSubClient by Nick O'Leary」をインストール。今回は2.7.0。
(3) 同じように、「arduinojson」で検索し、「ArduinoJson by Benoit Blanchon」をインストール。今回は6.10.1。
ESP32と自宅のWiFiの接続確認
下記スケッチをESP32に書き込みます。
#include <WiFiClientSecure.h>
const char* ssid = "XXXXXXXXX"; //your network SSID
const char* pass = "YYYYYYYYY"; //your network password
WiFiClientSecure client;void setup() {
// initialize serial:
Serial.begin(9600);// connect to WiFi AP
Serial.println("Attempting to connect to WPA network...");
Serial.print("SSID: ");
Serial.println(ssid);
WiFi.begin(ssid, pass);
Serial.print("WiFi connecting");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(100);
}Serial.println(" connected");
//Serial.print("HTTP Server: http://");
Serial.print("My IP address: ");
IPAddress myAddress = WiFi.localIP();
Serial.print(myAddress);
Serial.println("/");
}void loop() {
}
XXXXXXには自分の家(またはスマホのテザリング)のWiFiのSSDを、YYYYYYYにはそのパスワードを入れてください。
SSIDには日本語が含まれていてもOKでした。
シリアルポートを開き、うまくWiFiに接続できると、下記のようにIPアドレス(192.168.0.14)が表示されます。
このIPは、家庭WiFi内のローカルアドレスです。ESP32が、WiFi内の一つの装置として接続されて、家庭内ルーターのDHCPで動的IPが割り振られたということです。
また、このスケッチでは、ESP32はhttpサーバーを立ち上げて待ち受けています。
では、同じWiFi内にある他の装置から、ESP32への疎通チェックを行います。
疎通チェック1:Pingコマンド
PCでコマンドプロンプトを開いて、「ping 192.168.0.14」と打ちます。
192.168.0.14 に ping を送信しています 32 バイトのデータ:
192.168.0.14 からの応答: バイト数 =32 時間 =24ms TTL=255
192.168.0.14 からの応答: バイト数 =32 時間 =142ms TTL=255
192.168.0.14 からの応答: バイト数 =32 時間 =53ms TTL=255
上記のように表示されれば、通信OKです。
ためしに192.168.0.13にpingを打つと、通信しない場合の表示が確認できると思います。
疎通チェック2:ブラウザからアクセス
PCのブラウザから、「http://192.168.0.14」にアクセスします。
Chromeでは下記のように表示されました。これで疎通OKです。
このサイトにアクセスできません
192.168.0.14で接続が拒否されました
これは、ESP32のhttpサーバーにアクセスしたけれど、ESP32側で何も表示するものを用意していないため、このような表示になります。
ためしにhttp://192.168.0.13にアクセスすると、画面が切り替わるまでに時間がかかると思います。そんなアドレスのサーバーは誰もいないため、通信が迷子になって、時間がかかるためです。
AWS IoTの証明書を入手する
ここまでで、ESP32がWiFiに繋がる、つまりインターネットに繋がることが確認できました。
次は、自分のAWSアカウントのAWS IoTとの接続を試みます。
AWS IoTと通信するためには、クライアント側にAWS IoTの証明書3点を入れる必要があります。
ESP32にはファイル構造が無いため、証明書3点の中身をスケッチに平文でベタ書きすることで、ESP32がAWS IoTと通信できるようにします。
(セキュリティの観点から言うと、ESP32の中身をリバースエンジニアリングすれば、証明書を盗まれる可能性もあります。ESP32を物理的に盗まれうる場所に置くユースケースでは、この実装方法はオススメできません。)
AWS IoTの証明書は、AWSコンソールのトップページの下部「ソリューション」にある、
から、Zipでダウンロードできます。
僕はUbuntu用のものを、Windows Subsystem for Linux (WSL)のUbuntuにダウンロードしました。
詳細は下記記事をご覧ください。
AWS IoTをWSL(Windows Subsystem for Linux)のUbuntuで動かしてみた! - メタラーまとんがハイソにやらかすようです
ダウンロードして展開したものの中から、下記3種が証明書です。
・root-CA.crt
・thingname.cert.pem
・thingname.private.key
これらの中身を見れることを確認してから、次へ。
AWS IoTの設定
エンドポイントのコピー
AWSコンソールからIoT Coreを開き、「設定」からカスタムエンドポイント「~~~~~iot.us-west-2.amazonaws.com」をコピーしておきます。
セキュリティポリシーの設定
IoT Coreの「管理」->「モノ」から、今回使用するモノを開く。
「セキュリティ」から、証明書の一つを開く。(モノの登録時に作った一個だけがあるはず)
「ポリシー」から、アタッチされているポリシーを開く。
ここで、(1) Topic名「HighsoTopic」を許可する、(2)クライアント名「ESP32」を許可するポリシードキュメントを書きます。
具体的には下記を書きました。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iot:Publish",
"iot:Receive"
],
"Resource": [
"arn:aws:iot:us-west-2:0123456789:topic/*",
"arn:aws:iot:us-west-2:0123456789:topic/sdk/test/java",
"arn:aws:iot:us-west-2:0123456789:topic/sdk/test/Python",
"arn:aws:iot:us-west-2:0123456789:topic/topic_1",
"arn:aws:iot:us-west-2:0123456789:topic/topic_2"
]
},
{
"Effect": "Allow",
"Action": [
"iot:Subscribe"
],
"Resource": [
"arn:aws:iot:us-west-2:0123456789:topicfilter/*",
"arn:aws:iot:us-west-2:0123456789:topicfilter/sdk/test/java",
"arn:aws:iot:us-west-2:0123456789:topicfilter/sdk/test/Python",
"arn:aws:iot:us-west-2:0123456789:topicfilter/topic_1",
"arn:aws:iot:us-west-2:0123456789:topicfilter/topic_2"
]
},
{
"Effect": "Allow",
"Action": [
"iot:Connect"
],
"Resource": [
"arn:aws:iot:us-west-2:0123456789:client/ESP32",
"arn:aws:iot:us-west-2:0123456789:client/sdk-java",
"arn:aws:iot:us-west-2:0123456789:client/basicPubSub",
"arn:aws:iot:us-west-2:0123456789:client/sdk-nodejs-*"
]
}
]
}
クライアント名「ESP32」は別の名前でもよいのですが、これを許可しておかないと通信できません。
詳細は下記サイト様を参考にしました。
ESP32〜AWS IoTでMQTT通信して詰んだ話【ClientID】 - Qiita
AWS IoTとMQTT通信するスケッチ
下記は「ソラコムのボタンを押したら、ソレノイドがUnder The Seaを演奏し始める」スケッチです。
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
const char* ssid = "XXXXXXXX"; //your network SSID
const char* pass = "YYYYYYYY"; //your network passwordconst char* server = "ZZZZZZZZ.iot.us-west-2.amazonaws.com"; //end point
const int port = 8883;
WiFiClientSecure client;
PubSubClient mqttClient(client);long messageSentAt = 0;
int Value = 0;
char pubMessage[128];
char* inTopic = "HighsoTopic";
// Static buffer for JSON
StaticJsonDocument<200> doc;
char json[] = "{"message":"Initial message"}";
//Solenoid configuration
int TR = 27; //Output Pin
int note = 150; //Tempo
int claptime = 50;
int rest = note - claptime;
int flag = -1;
const char* Root_CA =
"-----BEGIN CERTIFICATE----- "
"******************* "
"******************* "
"******************* "
"-----END CERTIFICATE----- ";
const char* Client_cert =
"-----BEGIN CERTIFICATE----- "
"******************* "
"******************* "
"******************* "
"-----END CERTIFICATE----- ";
const char* Client_private =
"-----BEGIN RSA PRIVATE KEY----- "
"******************* "
"******************* "
"******************* "
"-----END RSA PRIVATE KEY----- ";
void connectAWSIoT() {
Serial.print("AWS IoT connecting...");
while (!mqttClient.connected()) {
if (mqttClient.connect("ESP32")) { //ClientID
Serial.println(" connected.");
} else {
Serial.print("Failed. Error state=");
Serial.print(mqttClient.state());
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void mqttSubscribe(char* TopicName){
int qos = 0;
Serial.print("Subscribing. topic: ");
Serial.print(TopicName);
Serial.print("...");
while(!mqttClient.subscribe(TopicName, qos)){
Serial.print(".");
delay(100);
}
Serial.println(" succeess.");
}
void mqttCallback (char* topic, byte* payload, unsigned int length) {
// Callback function for subscribe
Serial.print("Received. topic: ");
Serial.println(topic);
for (int i = 0; i < length; i++) {Serial.print( (char)payload[i] );
json[i] = (char)payload[i];
}
Serial.print(" ");
// Deserialize the JSON document
deserializeJson(doc, json);
const char* message = doc["message"];
Serial.print("Parsed message: ");
Serial.println(message);
// Action
flag = flag * -1;
}
void clap() {
digitalWrite(TR,HIGH);
delay(claptime);
digitalWrite(TR,LOW);
delay(rest);
}
void underTheSea() {
clap();
clap();
clap();
clap();
delay(note);
clap();
delay(note);
clap();
clap();
delay(note);
clap();
delay(note);
clap();
delay(note);
clap();
delay(note);
clap();
clap();
clap();
clap();
delay(note);
clap();
delay(note);
clap();
clap();
delay(note);
clap();
delay(note);
clap();
delay(note);
clap();
delay(note);delay(note);
delay(note);
delay(note);
delay(note);
}
void setup() {
// initialize serial:
Serial.begin(9600);// connect to WiFi AP
Serial.println("Attempting to connect to WPA network...");
Serial.print("SSID: ");
Serial.println(ssid);
WiFi.begin(ssid, pass);
Serial.print("WiFi connecting");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(100);
}
Serial.println(" connected");
//Serial.print("HTTP Server: http://");
Serial.print("My IP address: ");
IPAddress myAddress = WiFi.localIP();
Serial.print(myAddress);
Serial.println("/");
// connect to AWS IoT
client.setCACert(Root_CA);
client.setCertificate(Client_cert);
client.setPrivateKey(Client_private);
mqttClient.setServer(server, port);
mqttClient.setCallback(mqttCallback);connectAWSIoT();
mqttSubscribe(inTopic);
pinMode(TR,OUTPUT);
}
void loop() {
if (!mqttClient.connected()) {
Serial.println("Disconnected.");
connectAWSIoT();
mqttSubscribe(inTopic);
}
mqttClient.loop();
if(flag == 1){
underTheSea();
}
}
XXXXXとYYYYYにはWifiのSSIDとパスワードを記入してください。
ZZZZZZにはAWS IoTのエンドポイントをコピーしてください。
Root_CAなどの*******************には、証明書3点の中身を平文でコピーしてください。
ソレノイドを駆動するMOSEFTのゲートは27ピンに接続してください。今回使用する回路図は下記の通りです。
回路図の仕組みは下記の記事を参考にしてください。
ソレノイド(ZHO-0420S-05A4.5)をArduinoとMOSFETで動かしてみた! - メタラーまとんがハイソにやらかすようです
このスケッチでは、setup()でWiFiとAWS IoTに接続し、トピック名HighsoTopicをSubscribeします。
loop()のmqttClient.loop();で待ち受け状態になります。
何かをSubscribeすると、コールバック関数であるmqttCallback()が立ち上がります。受け取ったJSONを変換してprintしていますが、今回はメッセージ内容に関係なく、何かを受け取ったらアクションとしてflagを1/-1に切り替えています。
flagは最初は-1で、何かsubscribeして1になったとき、underTheSea()でソレノイドを演奏させます。
Lambdaの設定
ソラコムボタンを押した時に、Lambdaを起動してAWS IoTにpublishするようにします。
AWSコンソールからLambdaを開き、AWSIoTFullAccessを持ったロールで、出力をAWS IoTにして、下記Pythonスクリプトを登録しておきます。
# coding: utf-8
#ライブラリのimport
import json
import boto3
#Functionの開始を出力
print('Loading function')
#AWS IoT Data Planeオブジェクトを取得
iot = boto3.client('iot-data')
#Lambdaのメイン関数
def lambda_handler(event, context):
## TODO implement
#return {
# 'statusCode': 200,
# 'body': json.dumps('Hello from Lambda!')
#}
#トピックを指定
topic = 'HighsoTopic'
#メッセージの内容
payload = {
"message": "Hello from Lambda"
}
try:
#メッセージをPublish
iot.publish(
topic=topic,
qos=0,
payload=json.dumps(payload, ensure_ascii=False)
)
return "Succeeeded."
except Exception as e:
print(e)
return "Failed."
Topic名「HighsoTopic」に、payloadに格納したJSON("message": "Hello from Lambda")を送信しています。
このLambda関数を、AWS 1-clickを開いて、プロジェクトの動作として紐づけます。
Lambdaの詳細については、下記記事をご覧ください。
動作結果
ボタンを押したらアンダーザシーが流れる、ハイソボタンを作りました。SORACOMボタン→Lambda→AWS IoT→ESP32→ソレノイド pic.twitter.com/toIrUIHSkp
— メタラーまとん@はてなブログ (@Highso_ciety) 2019年5月6日
やっと完成しました!!
機能の考察
ボタンを押してからソレノイドが動き始めるまで、10秒ほどのタイムラグがあります。
ラグのほとんどは、ソラコムのボタンが緑に点灯するまで(LTEに繋がってAWS 1-clickを誘発するまで)にかかっており、その後のAWS IoT~ESP32のラグはほぼ無いように感じました。
拡張性の考察
今回は「ESP32がsubscribeしたらflagを1/-1に切り替えるアクション」を実装しました。
アクションの内容を、自分の所望のものに書き換えれば、色々と面白いことができそうです。
また、ソラコムボタンは「1回押し、2回押し、長押し」の3通りを使えますが、今回は区別していません。Lambdaで送るpayloadにこれらのメッセージを載せれば、ESP32が区別できるようになるはずです。
この記事のまとめ
- Arduino IDEでESP32の環境構築
- ESP32を自宅WiFiに繋いで接続確認
- AWS IoT Coreから証明書の入手、アクセスポイントの入手、ポリシーの設定
- ESP32で、AWS IoTと通信して、Topic名「HighsoTopic」をsubscribeするたびにflagを1/-1反転し、ソレノイドで曲を演奏するスケッチ
- ソラコムのボタンからLambdaでHightoTopicにPublishすうるPythonスクリプト
今回の実装を通して、様々なテクニックを身に付けました。
AWSはほぼ初めて使ったし、ESP32デビューも果たせました。
最後に、次の記事で、今回使ったスクリプト群をGitHubにアップロードして、GitHubデビューもしようと思います。
(はてなブログはソースコードに色付けができないので、ソースの共有に向いていないですね)
そして僕は、ソリューションアーキテクトを目指します。
以上、メタラーまとんでした。
ではでは。
今回使ったデバイス
waves ESP32 DevKitC V4 ESP-WROOM-32 ESP-32 WiFi BLE 技適取得済 国内発送
- 出版社/メーカー: Espressif Systems
- メディア: エレクトロニクス
- この商品を含むブログを見る
ソラコムボタンで遊んでみた記事まとめ
1. 【JAWS DAYS 2019参加レポ】「あのボタン」SORACOMの薄い本を買ってみた!
2. ソレノイド(ZHO-0420S-05A4.5)をArduinoとMOSFETで動かしてみた!
3. AWS IoTをWSL(Windows Subsystem for Linux)のUbuntuで動かしてみた!
4. ソラコムのボタンでUbuntuとサーバーレス通信してみた!(AWS IoT 1-click / Lambda / AWS IoT / Windows Subsystem for Linux: WSL)
5. ソラコムのボタンとESP32を繋いでソレノイドを演奏してみた!(AWS IoT 1-click / Lambda / AWS IoT / ESP32)