Register M5Stack to AWS IoT Core and Send Ultrasonic Sensor Data via MQTT to SNS for Email
arduinoiotawsI purchased an ESP32-based M5Stack Basic v2.7.
Following the tutorial, I installed Arduino IDE, configured the Board Manager settings and installed M5Stack Boards, selected Board=M5Core and Port from Tools, and installed the M5Unified library to complete the setup. When I uploaded the LongTextScroll example code from M5GFX, compilation started and text began scrolling on the device.
Let’s connect an ultrasonic distance sensor HC-SR04 and display the values. This sensor has VCC (power) and GND, plus two other terminals. When you send a 10μs pulse to Trig, it emits ultrasonic waves, and Echo stays HIGH until they bounce off an object and return, allowing you to calculate the distance. GPIO35 and 36 are input-only, so TRIG cannot be connected to them.
#include <M5Unified.h>
const int TRIG_PIN = 26;
const int ECHO_PIN = 36;
void setup() {
M5.begin();
M5.Display.setTextSize(2);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
}
void loop() {
M5.update();
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10); // You only need to supply a short 10uS pulse to the trigger input to start the ranging
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH);
float distance = duration * 0.034 / 2; // Test distance = high level time×velocity of sound (340M/S) / 2
M5.Display.fillScreen(BLACK);
M5.Display.setCursor(10, 50);
M5.Display.print("Distance: ");
M5.Display.print(distance);
M5.Display.println(" cm");
delay(500);
}
Let’s create AWS IoT Core resources. First, create a policy from Security.
$ THING_ID=M5Stack_01
$ AWS_ACCOUNT_ID=111122223333
$ AWS_REGION=ap-northeast-1
$ cat <<EOF
{
"Version":"2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iot:Connect",
"Resource": "arn:aws:iot:${AWS_REGION}:${AWS_ACCOUNT_ID}:client/${THING_ID}"
},
{
"Effect": "Allow",
"Action": "iot:Publish",
"Resource": [
"arn:aws:iot:${AWS_REGION}:${AWS_ACCOUNT_ID}:topic/\$aws/things/${THING_ID}/shadow/update",
"arn:aws:iot:${AWS_REGION}:${AWS_ACCOUNT_ID}:topic/\$aws/things/${THING_ID}/shadow/delete",
"arn:aws:iot:${AWS_REGION}:${AWS_ACCOUNT_ID}:topic/\$aws/things/${THING_ID}/shadow/get",
"arn:aws:iot:${AWS_REGION}:${AWS_ACCOUNT_ID}:topic/sensor/*"
]
},
{
"Effect": "Allow",
"Action": "iot:Receive",
"Resource": [
"arn:aws:iot:${AWS_REGION}:${AWS_ACCOUNT_ID}:topic/\$aws/things/${THING_ID}/shadow/update/accepted",
"arn:aws:iot:${AWS_REGION}:${AWS_ACCOUNT_ID}:topic/\$aws/things/${THING_ID}/shadow/delete/accepted",
"arn:aws:iot:${AWS_REGION}:${AWS_ACCOUNT_ID}:topic/\$aws/things/${THING_ID}/shadow/get/accepted",
"arn:aws:iot:${AWS_REGION}:${AWS_ACCOUNT_ID}:topic/\$aws/things/${THING_ID}/shadow/update/rejected",
"arn:aws:iot:${AWS_REGION}:${AWS_ACCOUNT_ID}:topic/\$aws/things/${THING_ID}/shadow/delete/rejected"
]
},
{
"Effect": "Allow",
"Action": "iot:Subscribe",
"Resource": [
"arn:aws:iot:${AWS_REGION}:${AWS_ACCOUNT_ID}:topicfilter/\$aws/things/${THING_ID}/shadow/update/accepted",
"arn:aws:iot:${AWS_REGION}:${AWS_ACCOUNT_ID}:topicfilter/\$aws/things/${THING_ID}/shadow/delete/accepted",
"arn:aws:iot:${AWS_REGION}:${AWS_ACCOUNT_ID}:topicfilter/\$aws/things/${THING_ID}/shadow/get/accepted",
"arn:aws:iot:${AWS_REGION}:${AWS_ACCOUNT_ID}:topicfilter/\$aws/things/${THING_ID}/shadow/update/rejected",
"arn:aws:iot:${AWS_REGION}:${AWS_ACCOUNT_ID}:topicfilter/\$aws/things/${THING_ID}/shadow/delete/rejected"
]
},
{
"Effect": "Allow",
"Action": [
"iot:GetThingShadow",
"iot:UpdateThingShadow",
"iot:DeleteThingShadow"
],
"Resource": "arn:aws:iot:${AWS_REGION}:${AWS_ACCOUNT_ID}:thing/${THING_ID}"
}
]
}
EOF
Create a Thing. Choose Auto-generate a new certificate, attach the policy to the device certificate, and download the device certificate, public/private keys, and root CA certificate. The keys can only be downloaded at creation time.
Get the MQTT broker endpoint.
$ aws iot describe-endpoint --endpoint-type iot:Data-ATS
{
"endpointAddress": "xxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com"
}
ESP32 supports Wi-Fi and Bluetooth. Set the certificate and private key in WiFiClientSecure, perform mTLS authentication with X.509 certificates on port 8883, and publish sensor data with the MQTT client PubSubClient.
Perform mTLS authentication with Istio Sidecar and access control by ServiceAccount - sambaiz-net
Topics don’t need to be registered in advance, but you must allow Publish in the Policy.
#include <M5Unified.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
// WiFi settings
const char* ssid = "xxxxxxxxx";
const char* password = "xxxxxxxxx";
const char* thing_id = "M5Stack_01";
// AWS IoT settings (need to update)
const char* mqtt_server = "xxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com";
const char* topic = "sensor/distance";
// Sensor pins
const int TRIG_PIN = 26;
const int ECHO_PIN = 35;
// Certificates
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-----
)";
WiFiClientSecure net;
PubSubClient client(net);
void connectWifi() {
WiFi.begin(ssid, password);
M5.Display.fillScreen(BLACK);
M5.Display.setCursor(0, 0);
M5.Display.println("WiFi connecting...");
int wifiRetry = 0;
while (WiFi.status() != WL_CONNECTED && wifiRetry < 50) {
delay(500);
M5.Display.print(".");
wifiRetry++;
}
if (WiFi.status() != WL_CONNECTED) {
M5.Display.fillScreen(RED);
M5.Display.setCursor(0, 0);
M5.Display.printf("WiFi Failed!\nCheck SSID/Password\n");
while(1) { delay(1000); }
}
delay(3000);
}
void connectAWSIoT() {
M5.Display.fillScreen(BLACK);
M5.Display.setCursor(0, 0);
M5.Display.println("AWS IoT connecting...");
net.setCACert(root_ca);
net.setCertificate(certificate);
net.setPrivateKey(private_key);
client.setServer(mqtt_server, 8883);
int mqttRetry = 0;
const int maxMqttRetry = 5;
while (!client.connected() && mqttRetry < maxMqttRetry) {
M5.Display.printf("Try %d/%d\n", mqttRetry + 1, maxMqttRetry);
if (client.connect(thing_id)) {
M5.Display.fillScreen(GREEN);
M5.Display.setCursor(0, 0);
M5.Display.printf("AWS IoT Connected!\nThing: %s\n", thing_id);
delay(3000);
return;
}
mqttRetry++;
delay(500);
}
if (!client.connected()) {
M5.Display.fillScreen(RED);
M5.Display.setCursor(0, 0);
M5.Display.printf("AWS IoT Failed!\nError: %d\n", client.state());
while(1) { delay(1000); }
}
}
float getDistance() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH);
return duration * 0.034 / 2;
}
void setup() {
M5.begin();
M5.Display.setTextSize(2);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
connectWifi();
connectAWSIoT();
}
void loop() {
// Keep MQTT connection
if (!client.connected()) {
connectAWSIoT();
}
client.loop();
float distance = getDistance();
char payload[100];
snprintf(payload, sizeof(payload),
"{\"distance\":%.2f,\"timestamp\":%lu}",
distance, millis());
M5.Display.fillScreen(client.publish(topic, payload) ? BLACK : RED);
M5.Display.setCursor(0, 0);
if (client.state() == 0) {
M5.Display.printf("Published!\nDistance: %.1f cm\n", distance);
} else {
M5.Display.printf("Publish Failed!\nState: %d\n", client.state());
}
delay(5000);
}
Verify that values are arriving by subscribing to the Topic in the MQTT test client.
Create a Message routing rule. A rule consists of SQL for the Topic and a destination. Various services such as SNS, S3, and Step Functions can be specified as destinations. Note that the Topic must be enclosed in single quotes or it won’t work, but it can be created without error even if not enclosed, so be careful.
When you register a Subscription with Protocol=Email in SNS, you’ll receive an email.