์ด๋ฒ ๊ธ์ ๐ Workshop:ESP32 with AWS IoT ์ ๋ด์ฉ์ ์ง์ ์ค์ตํ ๋ค ์ฌ ํฌ์คํ ํ ๋ด์ฉ์ ๋๋ค.
์ด๋ฒ ์ค์ต์์๋ ESP32์ Button์ ์ฅ์ฐฉํด ๋ฒํผ์ ๋๋ฅด๋ฉด MQTT ๋ฉ์์ง๋ฅผ AWS IoT์ ์ ์กํ๊ณ ๋ฉ์์ง๋ฅผ ํตํด AWS Lambda๋ฅผ ํธ๋ฆฌ๊ฑฐ ํ๋ ๋ฒ์ ์์๋ณด๋๋ก ํ์. ์์คํ ๊ตฌ์ฑ์ ์๋ ๊ทธ๋ฆผ๊ณผ ๊ฐ๋ค.
์ค์ ์๋ณธ Workshop์์๋ TTP223 ๋ผ๋ Touch ์ผ์๋ฅผ ์ด์ฉํด์ ์์คํ ์ ๊ตฌ์ฑํ์ง๋ง ์ด๋ฒ ์ค์ต์์๋ 4๋ฐ ์ค์์น๋ฅผ ์ด์ฉํ๊ธฐ๋ก ํ๋ค. ์ฌ๋ฃ๋ ์ด๊ณณ ์์ ๊ตฌ๋งคํ์ผ๋ฉฐ, ์ฝ 3๋ง์ ๋์ผ๋ก Arduino ๊ธฐ์ด ํคํธ๋ฅผ ๊ตฌ๋งคํ ์ ์๋ค.
Arduino ๊ธฐ์ด ํคํธ์์ ์ฌ์ฉํ ์ฌ๋ฃ๋ ๋ค์๊ณผ ๊ฐ๋ค.
220(10k)ฮฉ ์ ํญ์ pull down์ ํญ์ ๊ตฌ์ฑํ๊ธฐ ์ํด ๋ฃ์ ์ ํญ์ด๋ฉฐ, option์ผ๋ก ํ์ํ ์ด์ ๋ ESP32๋ ๋ด๋ถ pull up ํน์ pull down ์ ํญ์ ๊ฐ์ง๊ณ ์๊ธฐ ๋๋ฌธ์ด๋ค. pinMode(pin_number, INPUT_PULLDOWN)
ํน์ pinMode(pin_number, INPUT_PULLUP)
์ ์ด์ฉํด ํ ๋ชจ๋๋ฅผ ์ค์ ํด ์ฃผ๋ฉด ๋๋ค. ๋ชจ๋ GPIO port๊ฐ pull up ๋ฐ pull down ๋ ์ง์คํฐ๊ฐ ๋ฌ๋ ค์๋๊ฒ์ ์๋๋ค. ESP32 Datasheet๋ฅผ ์ดํด๋ณด๋ฉด
GPIO 34~39๋ฒ์ Input ์ ์ฉ์ด๋ฉฐ pull up ๋ฐ pull down์ด ์๋ค๊ณ ๋ช ์๋์ด ์๋ค.
pin_number๋ Input(ํน์ INPUT_PULLUP)์ผ๋ก ์ค์ ํ ๋ค ์์ ๊ฐ์ด pull up ์ ํญ์ ๋ฌ์์ฃผ๋ฉด Floating ์ํ๋ฅผ ๋ฐฉ์งํ ์ ์๋ค.
์ด์ ๋ฒํผ์ ๋๋ฌ ์ ๋๋ก ๋์ํ๋์ง ํ์ธํ๋ ์ฝ๋๋ฅผ ์์ฑํ๊ณ ํ์ธํด ๋ณด์.
int buttonPin = 4;
void setup() {
Serial.begin(115200);
pinMode(buttonPin, INPUT);
}
void loop() {
Serial.println(digitalRead(buttonPin));
}
์ ์ฝ๋๋ฅผ ์ ๋ก๋ ํ๋ฉด ๋ฒํผ์ ๋๋ฅผ๋ ๋ง๋ค ์๋ฆฌ์ผ ๋ชจ๋ํฐ์ 1์ด ์ถ๋ ฅ๋จ์ ํ์ธ ํ ์ ์๋ค.
์ด์ ๐ Tutorial์์ ์์ฑํ๋ ESP_AWS_IoT_Tutorial.ino
ํ์ผ์ ์ด์ง ๋ณ๊ฒฝํด ๋ณด์.
/*
Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "secrets.h"
#include <WiFiClientSecure.h>
#include <MQTTClient.h>
#include <ArduinoJson.h>
#include "WiFi.h"
// The MQTT topics that this device should publish/subscribe
#define AWS_IOT_PUBLISH_TOPIC "esp32/pub"
#define AWS_IOT_SUBSCRIBE_TOPIC "esp32/sub"
WiFiClientSecure net = WiFiClientSecure();
MQTTClient client = MQTTClient(256);
int buttonPushCounter = 0; // counter for the number of button presses
int buttonState = 0; // current state of the button
int lastButtonState = 0; // previous state of the button
int buttonPin = 4;
void connectAWS()
{
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.println("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED){
delay(500);
Serial.print(".");
}
// Configure WiFiClientSecure to use the AWS IoT device credentials
net.setCACert(AWS_CERT_CA);
net.setCertificate(AWS_CERT_CRT);
net.setPrivateKey(AWS_CERT_PRIVATE);
// Connect to the MQTT broker on the AWS endpoint we defined earlier
client.begin(AWS_IOT_ENDPOINT, 8883, net);
// Create a message handler
client.onMessage(messageHandler);
Serial.print("Connecting to AWS IOT");
while (!client.connect(THINGNAME)) {
Serial.print(".");
delay(100);
}
if(!client.connected()){
Serial.println("AWS IoT Timeout!");
return;
}
// Subscribe to a topic
client.subscribe(AWS_IOT_SUBSCRIBE_TOPIC);
Serial.println("AWS IoT Connected!");
}
void publishMessage()
{
StaticJsonDocument<200> doc;
doc["time"] = millis();
doc["sensor_a0"] = analogRead(0);
char jsonBuffer[512];
serializeJson(doc, jsonBuffer); // print to client
client.publish(AWS_IOT_PUBLISH_TOPIC, jsonBuffer);
}
void messageHandler(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
// StaticJsonDocument<200> doc;
// deserializeJson(doc, payload);
// const char* message = doc["message"];
}
void setup() {
Serial.begin(9600);
connectAWS();
}
void loop() {
// Setting value of buttonState what the D4 touch sensor is reporting
buttonState = touchRead(4);
// compare the buttonState to its previous state
if (buttonState != lastButtonState) {
// if the state has changed, increment the counter
if (buttonState == 1) {
// if the current state is 1 then the button went from off to on:
buttonPushCounter++;
Serial.println("on");
Serial.print("number of button pushes: ");
Serial.println(buttonPushCounter);
}
// Delay a little bit to avoid bouncing
delay(1258);
}
// save the current state as the last st/*
Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "secrets.h"
#include <WiFiClientSecure.h>
#include <MQTTClient.h>
#include <ArduinoJson.h>
#include "WiFi.h"
// The MQTT topics that this device should publish/subscribe
#define AWS_IOT_PUBLISH_TOPIC "esp32/pub"
#define AWS_IOT_SUBSCRIBE_TOPIC "esp32/sub"
WiFiClientSecure net = WiFiClientSecure();
MQTTClient client = MQTTClient(256);
int buttonPushCounter = 0; // counter for the number of button presses
int buttonState = 0; // current state of the button
int lastButtonState = 0; // previous state of the button
int buttonPin = 4;
void connectAWS()
{
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.println("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED){
delay(500);
Serial.print(".");
}
// Configure WiFiClientSecure to use the AWS IoT device credentials
net.setCACert(AWS_CERT_CA);
net.setCertificate(AWS_CERT_CRT);
net.setPrivateKey(AWS_CERT_PRIVATE);
// Connect to the MQTT broker on the AWS endpoint we defined earlier
client.begin(AWS_IOT_ENDPOINT, 8883, net);
// Create a message handler
client.onMessage(messageHandler);
Serial.print("Connecting to AWS IOT");
while (!client.connect(THINGNAME)) {
Serial.print(".");
delay(100);
}
if(!client.connected()){
Serial.println("AWS IoT Timeout!");
return;
}
// Subscribe to a topic
client.subscribe(AWS_IOT_SUBSCRIBE_TOPIC);
Serial.println("AWS IoT Connected!");
}
void publishMessage()
{
StaticJsonDocument<200> doc;
doc["time"] = millis();
doc["sensor_a0"] = analogRead(0);
char jsonBuffer[512];
serializeJson(doc, jsonBuffer); // print to client
client.publish(AWS_IOT_PUBLISH_TOPIC, jsonBuffer);
}
void messageHandler(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
// StaticJsonDocument<200> doc;
// deserializeJson(doc, payload);
// const char* message = doc["message"];
}
void setup() {
Serial.begin(115200);
connectAWS();
}
void loop() {
// Setting value of buttonState what the D4 touch sensor is reporting
buttonState = digitalRead(buttonPin);
// compare the buttonState to its previous state
if (buttonState != lastButtonState) {
// if the state has changed, increment the counter
if (buttonState == 1) {
// if the current state is 1 then the button went from off to on:
buttonPushCounter++;
Serial.println("on");
Serial.print("number of button pushes: ");
Serial.println(buttonPushCounter);
}
// Delay a little bit to avoid bouncing
delay(1258);
}
// save the current state as the last state, for next time through the loop
lastButtonState = buttonState;
}ate, for next time through the loop
lastButtonState = buttonState;
}
์ ์ฝ๋๋ฅผ ์ ๋ก๋ ํ๋ฉด AWS IoT์ ์ ์์ ํ์ง๋ง publish๋ฅผ ํ์ง๋ ์๊ณ ๋ฒํผ์ ๋๋ฅผ๋ ๋ง๋ค ๋ช๋ฒ ๋๋ ธ๋์ง๋ฅผ ์นด์ดํ ํด์ค๋ค.
์ด์ ๋ฐฉ๊ธ ์ ์
๋ก๋ํ ์ฝ๋ ์ผ๋ถ๋ฅผ ์์ ํด์ผํ๋ค. ๋ฒํผ์ ๋๋ฅผ๋ ๋ง๋ค Publish๋ฅผ ํ๊ณ ๋ฉ์์ง ๋ด์ฉ์ json ํ์์ผ๋ก {'Action': 'pressed'}
๋ณด๋ด๊ฒ ๋ด์ฉ์ ์์ ํด ๋ณด์.
void publishMessage()
{
char payload[512];
sprintf(payload,"{\"Action\":\"pressed\"}");
client.publish(AWS_IOT_PUBLISH_TOPIC, payload);
Serial.print("Published Message:");
Serial.println(payload);
}
๋ฉ์์ง๋ฅผ publishํ๋ ํจ์์
void loop() {
buttonState = touchRead(4);
// compare the buttonState to its previous state
if (buttonState != lastButtonState) {
// if the state has changed, increment the counter
if (buttonState == 1) {
// if the current state is 1 then the button went from off to on:=
buttonPushCounter++;
Serial.println("Button Pressed");
Serial.print("number of button pushes: ");
Serial.println(buttonPushCounter);
// Publish Message to AWS IoT
publishMessage();
Serial.println("==================");
}
// Delay a little bit to avoid bouncing
delay(1258);
}
// save the current state as the last state, for next time through the loop
lastButtonState = buttonState;
}
loop๋ฅผ ์์ ๊ฐ์ด ๋ฐ๊พธ๋ฉด ๋๋ค. ๐ Tutorial์์ ์งํํ๋ ๊ฒ ์ฒ๋ผ MQTT ํ ์คํธ ํด๋ผ์ด์ธํธ๋ฅผ ์คํํ๋ฉด ๋ค์ ์ฐ๋ฆฌ๊ฐ ์ํ๋ json ํ์์ ๋ฉ์์ง๋ฅผ ์์ ํ๋ฉด ์ฑ๊ณต์ด๋ค. ๐๐
AWS Lambda๋ Amazon Web Services์ ์ผ๋ถ๋ก Amazon์์ ์ ๊ณตํ๋ ์ด๋ฒคํธ ์ค์ฌ์ ์๋ฒ๋ฆฌ์ค ์ปดํจํ ํ๋ซํผ์ ๋๋ค. ์ด๋ฒคํธ์ ๋ํ ์๋ต์ผ๋ก ์ฝ๋๋ฅผ ์คํํ๊ณ ํด๋น ์ฝ๋์ ํ์ํ ์ปดํจํ ๋ฆฌ์์ค๋ฅผ ์๋์ผ๋ก ๊ด๋ฆฌํ๋ ์ปดํจํ ์๋น์ค์ ๋๋ค.
์ถ์ฒ: ์ํค๋ฐฑ๊ณผ
AWS Lambda๋ ์๋ฒ๋ฅผ ๋ฐ๋ก ๊ตฌ์ฑํ์ง ์๊ณ ์ฝ๋๋ฅผ ์คํํ ์ ์๋ AWS์์ ์ ๊ณตํ๋ ์ปดํจํฐ ์๋น์ค์ด๋ค. Lambda๋ฅผ ์ด์ฉํ๋ฉด ๊ฑฐ์ ๋ชจ๋ ์ ํ์ ์ ํ๋ฆฌ์ผ์ด์ ํน์ ๋ฐฑ์๋ ์๋น์ค์๋ํ ์ฝ๋๋ฅผ ์คํํ ์ ์๋ค. ์๋ฅผ ๋ค์ด ํน์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด Database์์ Query๋ฅผ ์ ์กํด ๋ฐ์ดํฐ๋ฅผ ์กฐํํ๋ ๋ฑ์ ์ฌ๋ฌ ์๋น์ค๋ฅผ ์คํํ ์์๋ค.
๋ชจ๋ ์ธ์ด๋ฅผ ์ง์ํ์ง๋ ์์ง๋ง ๋๋ต์ ์ผ๋ก ์ง์ํ๋ ์ธ์ด๋ ๋ค์๊ณผ ๊ฐ๋ค.
๊ฐ ์ธ์ด๋ค์ด ๋์๊ฐ๋ ์ด์์ฒด์ ํ๊ฒฝ์ Amazon Linux ํน์ Amazon Linux2 ์ง์ํ๋ ์ํคํ ์ณ๋ x86_64, arm64 ์ด๋ฉฐ, ๋ ์์ธํ ์ฌํญ์ ๐ ์ฌ๊ธฐ๋ฅผ ์ฐธ๊ณ ํ๋ฉด ๋๋ค.
AWS Lambda ํจ์๋ฅผ ์์ฑํ๊ธฐ์ํด AWS ๊ด๋ฆฌ ์ฝ์์์ Lambda ํญ์ผ๋ก ๋ค์ด๊ฐ ๋ณด์. Lambda ํญ์ ๋ชจ๋ ์๋น์ค
> ์ปดํจํ
> Lambda
๋ก ๋ค์ด๊ฐ ์ ์๋ค.
์ข์ธก์ AWS Lambda์์ ํจ์๋ฅผ ํด๋ฆญํ๋ค ํจ์ ์์ฑ ๋ฒํผ์ ๋๋ฌ์ ํจ์๋ฅผ ์๋ก ์์ฑํด๋ณด์.
ํจ์ ์ด๋ฆ์ IoT_Button ์ผ๋ก ์ง์ ํ๊ณ ๋ฐํ์์ Python 3.9๋ก ์ํคํ ์ณ๋ default์ธ x86_64๋ก ์ง์ ํ๋ค, ํจ์ ์์ฑ์ ํด๋ฆญํด ํจ์๋ฅผ ๋ง๋ ๋ค.
Lambda์์ ์คํํ ์ฝ๋๋ ์๋์ ๊ฐ์ด ์์ฑํ๋๋ก ํ์. โ๏ธ
import json
def lambda_handler(event, context):
print(json.dumps(event))
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
์์ฑํ ๋ค Deploy ๋ฒํผ์ ๋๋ฌ ๋ฐฐํฌํ๋ค ์ด ํจ์๋ฅผ ๊นจ์ธ ํธ๋ฆฌ๊ฑฐ๋ฅผ ๋ง๋ค์ด ๋ณด์.
AWS IoT Core์ ์ ์ํ๋ฉด ์ข์ธก ํญ์ ๋์
> ๊ท์น
์ผ๋ก ์ ์ํ ์ ์๋ค. ์ฌ๊ธฐ์ AWS IoT Core ๊ท์น ์์ง์ ์์ฑํด AWS Lambda๋ฅผ ํธ๋ฆฌ๊ฑฐ๋ง ํ ์ ์๋ ๊ท์น์ ๋ง๋ค ์ ์๋ค. ์์ฑ ๋ฒํผ์ ํด๋ฆญํด ๊ท์น์ ์์ฑํด๋ณด๋๋ก ํ์.
์ด๋ฆ์ IoTButtonRule ๋ก ์ง์ ํ๋ค ๊ท์น ์ฟผ๋ฆฌ ์ค๋ช ๋ฌธ์ ๋ค์๊ณผ ๊ฐ์ ์ฟผ๋ฆฌ๋ฅผ ์ง์ด ๋ฃ๋๋ค. ์ข ๋ ์์ธํ ๊ท์น ์ฟผ๋ฆฌ๋ฅผ ์๊ณ ์ถ๋ค๋ฉด AWS IoT SQL ์ฐธ์กฐ๋ฅผ ์ฐธ์กฐํ์.
SELECT * FROM 'esp32/pub' WHERE Action='pressed'
์ด์ ํ๋ ์ด์์ ์์ ์ค์ ๋์์ ์์ ์ถ๊ฐ ๋ฒํผ์ ํด๋ฆญํด์ AWS Lambda๋ฅผ ์ ํํด ๋ณด์.
์์ ์ ์ถ๊ฐํ๋ค ๋ณด๋ฉด ํจ์์ด๋ฆ์์ ์ฐ๋ฆฌ๊ฐ ๋ง๋ AWS Lambda์ธ IoT_Button์ด ์๋๊ฒ์ ํ์ธํ ์ ์์ ๊ฒ์ด๋ค.
์ด์ ์ ์ฒด ์์คํ ์ ํ ์คํธํด๋ณด๊ธฐ์ํด ๋ค์ AWS Lambda์ ๋ค์ด๊ฐ๋ณด์. ์ข ์ ๊ณผ ๋ค๋ฅด๊ฒ ํจ์ ๊ฐ์ ์ IoT_Button ์ AWS IoT ๊ฐ ๋ถ์ด์๋ ๊ฒ ์ ํ์ธ ํ ์์๋ค.
์ด์ ๋ชจ๋ํฐ๋ง์ ์ํด ๋ชจ๋ํฐ๋ง ํญ์ ๋ค์ด๊ฐ์ CloudWatch์์ ๋ก๊ทธ ๋ณด๊ธฐ ๋ฅผ ํด๋ฆญํด AWS CloudWatch ์๋น์ค์ ์ ์ํ์. ์ข์ธก ํญ์์ ๋ก๊ทธ
> ๋ก๊ทธ ๊ทธ๋ฃน
์ผ๋ก ๋ค์ด๊ฐ ๋ก๊ทธ ๊ทธ๋ฃน์ ์์ฑํด๋ณด์. ๋ก๊ทธ ๊ทธ๋ฃน ์ด๋ฆ์ /aws/lambda/IoT_Button
์ผ๋ก ์ค์ ํ๊ณ ๋๋จธ์ง ์ค์ ์ฌํญ์ default๋ก ์์ฑํ๋ค.
์ด์ ESP32์ Reset ๋ฒํผ์ ๋๋ฅด๊ณ ์ฐ๋ฆฌ๊ฐ ESP32์ ์ฅ์ฐฉํ๋ ์ค์์น๋ฅผ ๋๋ฌ๋ณด์. ๋๋ฅธ ๋ค AWS CloudWatch์์ /aws/lambda/IoT_Button๋ก๊ทธ ๊ทธ๋ฃน์ผ๋ก ๋ค์ด๊ฐ ๋ก๊ทธ ์คํธ๋ฆผ์ ๋ณด์. ๋ฒํผ์ ๋๋ฅธ ์๊ฐ ๊ธฐ์ค์ผ๋ก ์๋ก์ด ๋ก๊ทธ ์คํธ๋ฆผ์ด ์์ฑ๋ ์๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๋ก๊ทธ ์คํธ๋ฆผ์ผ๋ก ๋ค์ด๊ฐ๋ณด๋ฉด ๋ฌผ๋ฆฌ ์ค์์น๊ฐ ์ด 3๋ฒ ๋๋ฆฐ๊ฒ์ ํ์ธ ํ ์ ์๋ค.