ESP32 with AWS IoT -4-

์น˜์‚ผ์ดยท2021๋…„ 12์›” 8์ผ
1

Workshop:ESP32 with AWS IoT

๋ชฉ๋ก ๋ณด๊ธฐ
4/4
post-thumbnail

๐Ÿ‘จโ€๐Ÿ’ป Connecting AWS with ESP32: Create an IoT Lamp

์ด๋ฒˆ ๊ธ€์€ ๐Ÿ”— Workshop:ESP32 with AWS IoT ์˜ ๋‚ด์šฉ์„ ์ง์ ‘ ์‹ค์Šตํ•œ ๋’ค ์žฌ ํฌ์ŠคํŒ… ํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

1. ์‹œ์Šคํ…œ ๊ฐœ์š”

์ด๋ฒˆ ์‹ค์Šต์—์„œ๋Š” Web์„ ํ†ตํ•ด ESP32๋ฅผ ์ œ์–ดํ•˜๋Š” ์‹ค์Šต์— ๋Œ€ํ•ด ์ง„ํ–‰ํ•  ์˜ˆ์ •์ด๋‹ค. ์ด๋ฒˆ ์‹ค์Šต์—์„œ๋Š” Relay Module์„ ์ด์šฉํ•œ Lamp๋ฅผ ๋งŒ๋“ค์˜ˆ์ •์ด๋‹ค. ์ด์ „ 4๋ฐœ ์Šค์œ„์น˜๋ฅผ ์ด์šฉํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ–ˆ๋˜ ๐Ÿ”— Create an AWS IoT Button ์‹ค์Šต๊ณผ ํฐ ์ฐจ์ด์ ์ด ๋‘ ๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค.

  1. ์ฃผ ๋ฐ์ดํ„ฐ์˜ ํ๋ฆ„

    AWS IoT Button ์‹ค์Šต๋•Œ์˜ ์ฃผ๋œ ํ๋ฆ„์€ ESP32 ์—์„œ ๋ฐœ์ƒํ•œ ๋ฐ์ดํ„ฐ(๋ฒ„ํŠผ ๋ˆŒ๋ ธ๋‹ค๋Š” ๋ฉ”์‹œ์ง€ + ๋ˆŒ๋ฆฐ ์ˆ˜)๊ฐ€ AWS์˜ MQTT Broker์—๊ฒŒ ์ „๋‹ฌ๋˜ Lambda๋ฅผ ํŠธ๋ฆฌ๊ฑฐ๋ง ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ์จ ๊ฐ„๋‹จํžˆ ์„ค๋ช…ํ•ด ESP32 to AWS ๋ผ๊ณ  ํ•œ๋‹ค๋ฉด ์˜ค๋Š˜์˜ ์‹ค์Šต์€ AWS to ESP32 ์ด๋‹ค.

  2. ์‹œ์Šคํ…œ ์ปดํฌ๋„ŒํŠธ์˜ ์ˆ˜

    ์ €๋ฒˆ ์‹ค์Šต์—๋Š” ์‹œ์Šคํ…œ์— AWS Lambda ๋ฅผ ์ด์šฉํ•ด ๊ฐ„๋‹จํ•œ ๋กœ๊ทธ๋ฅผ ์ถœ๋ ฅํ–ˆ๋‹ค๋ฉด, ์ด๋ฒˆ์—๋Š” AWS Lambda, AWS API Gatyeway, AWS 3S(Simple Stroage Service) ์™€ AWS Device Shadow ๊ฐ€ ์‹œ์Šคํ…œ์˜ ์ปดํฌ๋„ŒํŠธ๋กœ ๋“ค์–ด๊ฐ„๋‹ค.

์‹œ์Šคํ…œ ๊ตฌ์„ฑ์€ ์œ„ ์™€ ๊ฐ™๋‹ค. ์‚ฌ์šฉ์ž๋Š” API Gateway๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„์— ์ ‘์†ํ•œ๋’ค, ์ ‘์†๋œ ์„œ๋ฒ„์—์„œ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ Lamp๋ฅผ ์ œ์–ดํ•˜๋Š” ์‹œ์Šคํ…œ์ด๋‹ค.

2. Relay Module

์ „๊ธฐ Lamp๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ์š”์†Œ ์ค‘ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ์š”์†Œ๋Š” ์ „๊ตฌ ํ˜น์€ LED ์ผ ๊ฒƒ ์ด๋‹ค. ๋งŒ์•ฝ ์ด๋Ÿฌํ•œ ์ „๊ตฌ๊ฐ€ 220V 60W์˜ ์ „์›์„ ์š”๊ตฌํ•œ๋‹ค๋ฉด ESP32๋กœ ์–ด๋–ป๊ฒŒ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์„๊นŒ? ๊ฐ€์žฅ ์†์‰ฌ์šด ๋ฐฉ๋ฒ•์€ Relay Module์„ ์ด์šฉํ•˜๋Š” ๊ฒƒ ์ด๋‹ค.

Relay Module์€ ์ œ์–ด์‹ ํ˜ธ(SIG)๋ฅผ ์ด์šฉํ•ด ์ผ๋ฐ˜ ๊ฐ€์ •์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ„๋‹จํ•œ 220V ๊ธฐ๊ธฐ๋ฅผ On/Off ํ•  ์ˆ˜ ์žˆ๋‹ค. Relay Module์— VCC์™€ GND๋ฅผ ๊ฐ๊ฐ 5V(3.3V) ์™€ GND๋ฅผ ์—ฐ๊ฒฐํ•˜๊ณ  SIG๊ฐ€ LOW์ผ ๋•Œ, COM ๋‹จ์ž์™€ NC(Normal Close)๊ฐ€ ์ ‘์ง€์ธ ์ƒํƒœ ์ฆ‰ ์—ฐ๊ฒฐ์ด ๋˜์–ด ์žˆ๋‹ค. ์ด๋•Œ ์ œ์–ด ์‹ ํ˜ธ๊ฐ€ HIGH ์ƒํƒœ๋กœ ๋ฐ”๋€Œ๋ฉด COM๊ณผ NO(Normal Open)์ด ์—ฐ๊ฒฐ๋œ๋‹ค.

2.1 Relay Test

๋งŒ์•ฝ ์ง„์งœ ๊ฐ€์ •์šฉ ์ฝ˜์„ผํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ „๊ตฌ๋กœ ๋ฌด๋“œ๋“ฑ์„ ๋งŒ๋“ค๊ณ  ์‹ถ๋‹ค๋ฉด 220V ์ „๊ตฌ ์†Œ์ผ“์˜ ํ”ผ๋ณต์„ ๋ฒ—๊ธด๋’ค COM๊ณผ NO์— ์—ฐ๊ฒฐ์„ ํ•˜๋ฉด ๋œ๋‹ค. ์ด๋ฒˆ ์‹ค์Šต์—์„œ๋Š” LED๋ฅผ ์ „๊ตฌ ๋Œ€์‹  ์‚ฌ์šฉํ•˜๋Š”๊ฒƒ์œผ๋กœ ์ง„ํ–‰ํ•œ๋‹ค.

Relay์˜ ๋™์ž‘ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์—…๋กœ๋“œํ•ด Relay๊ฐ€ ์ž˜ ์ž‘๋™ ๋˜๋Š”์ง€ ํ™•์ธํ•ด ๋ณด์ž.

int signalPin = 16;

void setup() {
    Serial.begin(115200);
    delay(2000);
    Serial.println("Lelay Module Test");
    pinMode(signalPin, OUTPUT);
}

void loop() {
    Serial.println("On");
    digitalWrite(signalPin, HIGH); 
    delay(1000); 
    Serial.println("Off");
    digitalWrite(signalPin, LOW); 
    delay(1000); 
}

3. AWS IoT Device Shadow

AWS IoT์—๋Š” Device Shadow๋ผ๋Š” ๊ธฐ๋Šฅ์ด ์žˆ๋‹ค. AWS IoT Core์˜ ๊ฐœ๋ฐœ์ž ์•ˆ๋‚ด์„œ์— Device Shadow Service์˜ ๋‹จ๋ฝ์„ ์ฝ์–ด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์จ์žˆ๋‹ค.

The AWS IoT Device Shadow service adds shadows to AWS IoT thing objects. Shadows can make a deviceโ€™s state available to apps and other services whether the device is connected to AWS IoT or not. AWS IoT thing objects can have multiple named shadows so that your IoT solution has more options for connecting your devices to other apps and services.

์ถœ์ฒ˜: AWS IoT Core Developer - Guide Deivce Shadow Service

๋””๋ฐ”์ด์Šค์˜ online/offline์— ๊ด€๊ณ„์—†์ด ๋””๋ฐ”์ด์Šค์˜ ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์„œ๋น„์Šค๋ผ๊ณ  ์ ํ˜€์žˆ๋‹ค. ๋งˆ์น˜ ๋””๋ฐ”์ด์Šค๊ฐ€ ์ธํ„ฐ๋„ท์— ์—ฐ๊ฒฐ์œ ๋ฌด์— ์ƒ๊ด€์—†์ด ์ œ์–ด๋ฅผ ํ•  ์ˆ˜์žˆ๋Š” ๊ฒƒ ์ฒ˜๋Ÿผ ์˜คํ•ดํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ ์ž˜ ์ฝ์–ด๋ณด๋ฉด ๋””๋ฐ”์ด์Šค๊ฐ€ ์˜จ๋ผ์ธ์ด๋˜ ์˜คํ”„๋ผ์ธ์ด๋˜ ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์„œ๋น„์Šค๋ผ๊ณ  ์ ํ˜€์žˆ๋‹ค.

Device Shadow Service๋ฅผ ์ด์šฉํ•˜๋ฉด ๋‚ด๊ฐ€ ์†ก์‹ ํ•œ ๋ฉ”์‹œ์ง€๋Š” ์‹ค์ œ ์‚ฌ๋ฌผ์— ์ „๋‹ฌ๋˜์ง€ ์•Š๊ณ  Device Shadow์— ์ „๋‹ฌ์ด ๋œ๋‹ค. ์ด๋ ‡๊ฒŒ ์ „๋‹ฌ ๋œ ๋ฉ”์‹œ์ง€๋Š” ํ˜„์žฌ ์‚ฌ๋ฌผ์˜ ์ƒํƒœ์™€ ๋น„๊ตํ•˜์—ฌ ์ฐจ์ด(๋ธํƒ€)๋ฅผ ๊ณ„์‚ฐํ•œ ํ›„ ์‚ฌ๋ฌผ์—๊ฒŒ ์ „๋‹ฌ๋˜์–ด ์ง„๋‹ค.

Device Shadow ์€ JSON์œผ๋กœ ์ด๋ฃจ์–ด์ง„ ๋ฌธ์„œ์ด๋ฉฐ, ์ด JSON ๋ฌธ์„œ์— ์‚ฌ๋ฌผ์˜ ํ˜„์žฌ ์ƒํƒœ ๋ฐ”๋€”์ƒํƒœ ๋‘ ์ƒํƒœ๊ฐ„์˜ ์ฐจ์ด๋ฅผ ๊ธฐ์ˆ ํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ ์„ธ ์„น์…˜์€ ๊ฐ๊ฐ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  1. Desired: ์‚ฌ์šฉ์ž๊ฐ€ ์›ํ•˜๋Š” ์ƒํƒœ
  2. Reported: ์‚ฌ๋ฌผ์˜ ํ˜„์žฌ ํ˜•ํƒœ
  3. Delta: Desired์™€ Reported๊ฐ„์˜ ์ฐจ์ด

3.1 Sample Flow

Sample Flow๋ฅผ ํ†ตํ•ด ๊ฐ„๋‹จํ•œ ์˜ˆ์‹œ๋ฅผ ๋“ค์–ด๋ณด์ž. ์ฒ˜์Œ Device Shadow์˜ JSON ๋ฌธ์„œ๊ฐ€ ์•„๋ž˜์™€ ๊ฐ™์ด ์ ํ˜€ ์žˆ๋‹ค.

{
  "desired": {
    "status": "on"
  },
  "reported": {
    "status": "on"
  }
}

์ด์ œ ์‚ฌ์šฉ์ž๊ฐ€ Web ํ˜น์€ ๊ธฐํƒ€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ์‚ฌ๋ฌผ์˜ ์ƒํƒœ๋ฅผ ๋ฐ”๊พธ๋ คํ•œ๋‹ค. ์ด๋ฒˆ ์‹ค์Šต์—์„œ๋Š” Web์— ์žˆ๋Š” ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ Lamp๋ฅผ ํ‚ค๋ ค๊ณ  ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™๋‹ค. ์ด๋Ÿฌํ•œ desired ์ƒํƒœ๊ฐ€ AWS IoT๋กœ ์ „์†ก์ด ๋  ๊ฒƒ ์ด๋‹ค.

{
    "state": {
        "desired": {
            "status": "off"
        }
    }
}

desired ์™€ reported์˜ ์ƒํƒœ ๋ถˆ์ผ์น˜๋Š” ๋‘ ์ƒํƒœ์˜ ์ฐจ์ด๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ delta๊ฐ€ ๊ณ„์‚ฐ๋œ๋‹ค. delat ์ƒํƒœ๊ฐ€๋˜๋ฉด ๊ทธ ์ฆ‰์‹œ ์ž์‹ ์˜ ์‚ฌ๋ฌผ์—๊ฒŒ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ† ํ”ฝ์œผ๋กœ $aws/things/{THING_NAME}/shadow/update/delta (ํ˜น์€ $aws/things/{THING_NAME}/shadow/{SHADOW_NAME}/update/delta) ์•„๋ž˜ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐœํ–‰ํ•œ๋‹ค.

{
  "version": 463,
  "timestamp": 1571747274,
  "state": {
    "status": "off"
  },
  "metadata": {
    "status": {
      "timestamp": 1571747274
    }
  }
}

์ด ๋ฉ”์‹œ์ง€๋ฅผ ์ˆ˜์‹ ํ•œ ์‚ฌ๋ฌผ์€ ๊ทธ ์ฆ‰์‹œ desired์— ์žˆ๋Š” status๋กœ ์‹ค์ œ ์ƒํƒœ(์—ฌ๊ธฐ์„œ๋Š” Lamp์˜ ์†Œ๋“ฑ)๋ฅผ ๋ฐ˜์˜ํ•œ ๋’ค $aws/things/{THING_NAME}/shadow/update (ํ˜น์€ $aws/things/{THING_NAME}/shadow/{SHADOW_NAME}/update )๋กœ reported๋ฅผ ์ „์†กํ•œ๋‹ค.

{
    "state": {
        "reported": {
            "status": "off"
        }
    }
}

reported๋ฅผ ๋ฐ›์€ AWS IoT๋Š” ์ด๋ฅผ ๋ฐ˜์˜ํ•ด Device Shadow์— Document์— ๋ฐ˜์˜ํ•œ๋‹ค.

{
  "desired": {
    "status": "off"
  },
  "reported": {
    "status": "off"
  }
}

3.2 Device Shadow ์ƒ์„ฑ

์ด์ œ ์‹ค์ œ Device Shadow๋ฅผ ์ƒ์„ฑํ•ด ๋ณด์ž. AWS IoT Core๋กœ ์ ‘์† ํ•œ ๋’ค ์ขŒ์ธก ํŒจ๋„์—์„œ ๊ด€๋ฆฌ โžœ ์‚ฌ๋ฌผ ์—์„œ ์ž์‹ ์ด ๋งŒ๋“ค์—ˆ๋˜ ์‚ฌ๋ฌผ(์—ฌ๊ธฐ์„œ๋Š” ESP32) ์„ ํด๋ฆญํ•ด๋ณด์ž.

์„€๋„์šฐ ์ƒ์„ฑ ์„ ํด๋ฆญํ•œ ๋’ค, ์ด๋ฆ„ ์—†๋Š”(ํด๋ž˜์‹) ์„€๋„์šฐ๋ฅผ ์ƒ์„ฑํ•ด ๋ณด์ž. ๐Ÿ”— Workshop:ESP32 with AWS IoT ์—์„œ๋Š” ํด๋ž˜์‹๊ณผ ๋„ค์ž„๋“œ ์„€๋„์šฐ์˜ ๊ตฌ๋ถ„์„ ๋”ฐ๋กœ ์ง€์ •ํ•˜์ง€ ์•Š์•˜๋Š”๋ฐ ์ด๊ฒŒ ๋ฒ„์ „์ด ์˜ฌ๋ผ๊ฐ€์„œ ์ƒ๊ธด ์ฐจ์ด์ธ์ง€ ํ˜น์€ ์› ์ €์ž๊ฐ€ ์ผ๋ถ€๋Ÿฌ ์–ธ๊ธ‰์„ ์•ˆํ•œ๊ฑด์ง€ ์•Œ ์ˆ˜๋Š” ์—†๋‹ค. ๋„ค์ž„๋“œ ์„€๋„์šฐ๋ฅผ ์ƒ์„ฑํ•ด ์‚ดํŽด๋ณด๋ฉด์บก

๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ† ํ”ฝ์ฐจ์ด๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์› ์ €์ž๊ฐ€ $aws/things/ESP32/shadow ๋ผ๋Š” ํ† ํ”ฝ ์ ‘๋‘์‚ฌ๋ฅผ ๊ณ„์† ์‚ฌ์šฉํ•œ๊ฒƒ์œผ๋กœ๋ณด์•„ ํด๋ž˜์‹ ์„€๋„์šฐ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด ๋œ๋‹ค.

์ฐธ๊ณ ๋กœ $aws๋Š” ์‹œ์Šคํ…œ ํ˜น์€ ํžˆ๋“  ํ† ํ”ฝ์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค(MQTT์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์™€์ผ๋“œ ์นด๋“œ๋Š” ์•„๋‹˜).

Device Shadow๋ฅผ ์ƒ์„ฑํ•˜๋ฉด ๋””๋ฐ”์ด์Šค ์„€๋„์šฐ ๋ฌธ์„œ ํƒญ์— ๋“ค์–ด๊ฐ€๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ดˆ๊ธฐ ์ƒํƒœ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

๋””๋ฐ”์ด์Šค ์„€๋„์šฐ ์ƒํƒœ

{
  "state": {
    "desired": {
      "welcome": "aws-iot"
    },
    "reported": {
      "welcome": "aws-iot"
    }
  }
}

๋””๋ฐ”์ด์Šค ์„€๋„์šฐ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ

{
  "metadata": {
    "desired": {
      "welcome": {
        "timestamp": 1638861952
      }
    },
    "reported": {
      "welcome": {
        "timestamp": 1638861952
      }
    }
  }
}

4. ESP32 Setting ๋ฐ Policy Update

์ด์ œ ์‚ฌ๋ฌผ์— ์ฝ”๋“œ๋ฅผ ์—…๋กœ๋“œํ•œ๋’ค Device Shadow์˜ ํ† ํ”ฝ ์‚ฌ์šฉ์„ ์œ„ํ•ด Policy๋ฅผ ์—…๋ฐ์ดํŠธ ํ•˜์ž. ์šฐ์„  ์•„๋ž˜์˜ ์ฝ”๋“œ๋ฅผ ESP32์— ์—…๋กœ๋“œ ํ•œ๋‹ค.

/*
  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   "$aws/things/ESP32/shadow/update"
#define AWS_IOT_SUBSCRIBE_TOPIC "$aws/things/ESP32/shadow/update/delta"

int msgReceived = 0;
int signalPin = 16;
String rcvdPayload;
char sndPayloadOff[512];
char sndPayloadOn[512];

WiFiClientSecure net = WiFiClientSecure();
MQTTClient client = MQTTClient(256);

void connectAWS()
{
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.println("");
  Serial.println("###################### Starting Execution ########################");
  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.println("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 messageHandler(String &topic, String &payload) {
  msgReceived = 1;
  rcvdPayload = payload;
}

void setup() {
  Serial.begin(115200);
  pinMode(signalPin, OUTPUT);
  digitalWrite(signalPin, LOW);
  
  sprintf(sndPayloadOn,"{\"state\": { \"reported\": { \"status\": \"on\" } }}");
  sprintf(sndPayloadOff,"{\"state\": { \"reported\": { \"status\": \"off\" } }}");
  
  connectAWS();
  
  Serial.println("Setting Lamp Status to Off");
  client.publish(AWS_IOT_PUBLISH_TOPIC, sndPayloadOff);
  
  Serial.println("##############################################");
}

void loop() {
   if(msgReceived == 1)
    {
//      This code will run whenever a message is received on the SUBSCRIBE_TOPIC_NAME Topic
        delay(100);
        msgReceived = 0;
        Serial.print("Received Message:");
        Serial.println(rcvdPayload);
        StaticJsonDocument<200> sensor_doc;
        DeserializationError error_sensor = deserializeJson(sensor_doc, rcvdPayload);
        const char *sensor = sensor_doc["state"]["status"];
 
        Serial.print("AWS Says:");
        Serial.println(sensor); 
        
        if(strcmp(sensor, "on") == 0)
        {
         Serial.println("IF CONDITION");
         Serial.println("Turning Lamp On");
         digitalWrite(signalPin, HIGH);
         client.publish(AWS_IOT_PUBLISH_TOPIC, sndPayloadOn);
        }
        else 
        {
         Serial.println("ELSE CONDITION");
         Serial.println("Turning Lamp Off");
         digitalWrite(signalPin, LOW);
         client.publish(AWS_IOT_PUBLISH_TOPIC, sndPayloadOff);
        }
      Serial.println("##############################################");
    }
  client.loop();
}

์ด์ œ AWS IoT Core์—์„œ ์ขŒ์ธก ํŒจ๋„์— ๋ณด์•ˆ โžœ ์ •์ฑ… ์ง€๋‚œ ์‹ค์Šต ๐Ÿ”— Connecting AWS with ESP32: Tutorial ์—์„œ ์ƒ์„ฑํ–ˆ๋˜ ESP32 Policy ๋ฅผ ์ƒˆ๋กœ ์—…๋ฐ์ดํŠธํ•œ๋‹ค.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:Connect",
      "Resource": "arn:aws:iot:REGION:ACCOUNT_ID:client/${iot:Connection.Thing.ThingName}"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Publish",
      "Resource": [
        "arn:aws:iot:REGION:ACCOUNT_ID:topic/esp32/pub",
        "arn:aws:iot:REGION:ACCOUNT_ID:topic/$aws/things/${iot:Connection.Thing.ThingName}/shadow/update"
      ]
    },
    {
      "Effect": "Allow",
      "Action": "iot:Subscribe",
      "Resource": [
        "arn:aws:iot:REGION:ACCOUNT_ID:topicfilter/esp32/sub",
        "arn:aws:iot:REGION:ACCOUNT_ID:topicfilter/$aws/things/${iot:Connection.Thing.ThingName}/shadow/update/delta"
      ]
    },
    {
      "Effect": "Allow",
      "Action": "iot:Receive",
      "Resource": [
        "arn:aws:iot:REGION:ACCOUNT_ID:topic/esp32/sub",
        "arn:aws:iot:REGION:ACCOUNT_ID:topic/$aws/things/${iot:Connection.Thing.ThingName}/shadow/update/delta"
      ]
    }
  ]
}

REGION ๊ณผ ACCOUNT_ID ๋ฅผ ๋ชจ๋ฅด๊ฒ ์œผ๋ฉด ์ •์ฑ… ARN ๋ž€์— ์žˆ๋Š” ARN์—์„œ ํ™•์ธํ•˜์ž.

arn:aws:iot:REGION:ACCOUNT_ID:policy/...

${iot:Connection.Thing.ThingName} ์šฐ๋ฆฌ๊ฐ€ ์—ฐ๊ฒฐํ•˜๋Š” ์‚ฌ๋ฌผ์˜ ์ด๋ฆ„์ด๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ์‚ฌ๋ฌผ์˜ ์ด๋ฆ„์€ ESP32 ์ด๋ฏ€๋กœ ESP32๋ฅผ ๋Œ€์‹  ๋„ฃ์–ด์ฃผ๋ฉด ๋œ๋‹ค.

5. Create Lambda Function

์ด์ œ 2 ์ข…๋ฅ˜์˜ Lambda Function์„ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค. ํ•˜๋‚˜๋Š” Web์—์„œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„๋•Œ API Gateway๋ฅผ ํ†ตํ•ด Desired ์ƒํƒœ๋ฅผ ์ „๋‹ฌํ•ด์ฃผ๋Š” Lambda ์™€ EPS32์˜ ์ƒํƒœ๋ฅผ ์ฒดํฌํ•ด์ฃผ๋Š” Lambda ์ด๋‹ค. ์—ฌ๊ธฐ์„œ boto3 ๋ž€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ import ํ•˜๋Š”๋ฐ boto3๋Š” python ์šฉ AWS SDK์ด๋‹ค.

Shadow Status Check

import json
import boto3
import logging
 
logger = logging.getLogger()
logger.setLevel(logging.INFO)
 
client = boto3.client('iot-data')
 
def lambda_handler(event, context):
    
    # print(json.dumps(event))
    print("Thing Name: " + json.dumps(event['queryStringParameters']['thingname']))
    
    logger.info("Attempting to fetch Shadow State")
    
    THINGNAME=event['queryStringParameters']['thingname']
    if (THINGNAME == ""):
        print("No Thing Name found. Setting Thing Name = ESP32")
        THINGNAME="ESP32"
    
    try:
        response = client.get_thing_shadow(thingName=THINGNAME)
        logger.info("Shadow State Received")
        res = response['payload'].read()
        res_json = json.loads(res)
        print(json.dumps(res_json))
        
        status = res_json['state']['reported']
        
        logger.info("Received From IoT: " + json.dumps(status))
        
        logger.info("\nChanging for website\n")
     
        value = status['status']
        if (value == "on"):
            status['status'] = "It's On"
        else:
            status['status'] = "It's Off"
        
        logger.info("Sending to Website: " + json.dumps(status) + "\n")
        
        return {
            'statusCode': 200,
            "headers": {
                'Access-Control-Allow-Origin':'*',
                'Access-Control-Allow-Headers':'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
                'Access-Control-Allow-Methods':'GET,OPTIONS'
            },
            'body': json.dumps(status)
        }
    except:
        status = {"status": "Device Shadow State Error"}
        return {
            'statusCode': 200,
            "headers": {
                'Access-Control-Allow-Origin':'*',
                'Access-Control-Allow-Headers':'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
                'Access-Control-Allow-Methods':'GET,OPTIONS'
            },
            'body': json.dumps(status)
        }

Shadow Update

import json
import boto3
import logging
 
logger = logging.getLogger()
logger.setLevel(logging.INFO)
 
client = boto3.client('iot-data')
 
def lambda_handler(event, context):
    print(json.dumps(event['body']))
    
    body = event['body']
    body = json.loads(body)
    
    THINGNAME=body['thingname']
    if (THINGNAME == ""):
        print("No Thing Name found. Setting Thing Name = ESP2")
        THINGNAME="ESP32"
    
    if body['action'] == "on":
        payload = json.dumps({'state': { 'desired': { 'status': 'on' } }})
        
        logger.info("Attempting to Update Shadow State to ON")
        response = client.update_thing_shadow(
            thingName=THINGNAME,
            payload=payload
        )
        logger.info("IOT Shadow Updated")
    else:
        payload = json.dumps({'state': { 'desired': { 'status': 'off' } }})
        
        logger.info("Attempting to Update Shadow State to OFF")
        response = client.update_thing_shadow(
            thingName=THINGNAME,
            payload=payload
        )
        logger.info("IOT Shadow Updated")
        
    
    return {
        'statusCode': 200,
        "headers": {
            'Access-Control-Allow-Origin':'*',
            'Access-Control-Allow-Headers':'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
            'Access-Control-Allow-Methods':'GET,OPTIONS'
        },
        'body': json.dumps('Shadow Updated!')
    }

Lambda ์ƒ์„ฑ ๋ฐฉ๋ฒ•์€ ๐Ÿ”— Create an AWS IoT Button ์‹ค์Šต ํŽธ์„ ์ฐธ๊ณ ํ•˜์ž. ์ด์ œ ์šฐ๋ฆฌ์˜ Lambda Function์ด AWS IoT Data์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด

๐Ÿ”— AWSIoTDataAccess ์—์„œ ์ •์ฑ…์‚ฌ์šฉ์— Lambda๋ฅผ ๋„ฃ์–ด ์ค˜์•ผํ•œ๋‹ค. ๋งํฌ๋ฅผ ํƒ€๊ณ  ๋“ค์–ด๊ฐ€๋„ ๋˜๋ฉฐ, AWS ๊ด€๋ฆฌ ์ฝ˜์†”์— ๋กœ๊ทธ์ธํ•œ ๋’ค, AWS ์„œ๋น„์Šค โžœ ๋ชจ๋“  ์„œ๋น„์Šค โžœ IAM์— ์ ‘์†ํ•œ๋‹ค(IAM์€ ๋ชจ๋“  ์„œ๋น„์Šค ๋ž€์— ๋ณด์•ˆ, ์ž๊ฒฉ ์ฆ๋ช… ๋ฐ ๊ทœ์ • ์ค€์ˆ˜ ์—์„œ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค). ์ด์ œ ์ขŒ์ธก ํŒจ๋„์— ์•ก์„ธ์Šค ๊ด€๋ฆฌ โžœ ์ •์ฑ… ์œผ๋กœ ๋“ค์–ด๊ฐ€์„œ AWSIoTDataAcces ์ •์ฑ…์„ ๊ฒ€์ƒ‰ํ•œ๋’ค ์ •์ฑ… ์‚ฌ์šฉํƒญ์—์„œ ์—ฐ๊ฒฐ์„ ํด๋ฆญํ•ด ๋ฐฉ๊ธˆ ๋งŒ๋“  Lambda Function ์„ ๋“ฑ๋กํ•œ๋‹ค.

6. AWS API Gateway

6.1 API Gateway ๋ž€?

API Gateway๋Š” API ์„œ๋ฒ„ ์•ž๋‹จ์—์„œ ๋ชจ๋“  API ์„œ๋ฒ„๋“ค์˜ ์—”๋“œํฌ์ธํŠธ๋ฅผ ๋‹จ์ผํ™” ํ•ด์ฃผ๋Š” ๋˜ ๋‹ค๋ฅธ ์„œ๋ฒ„์ž…๋‹ˆ๋‹ค. API์— ๋Œ€ํ•œ ์ธ์ฆ๊ณผ ์ธ๊ฐ€ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ, ๋ฉ”์„ธ์ง€์˜ ๋‚ด์šฉ์— ๋”ฐ๋ผ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด๋ถ€์— ์žˆ๋Š” ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋กœ ๋ผ์šฐํŒ…ํ•˜๋Š” ์—ญํ• ์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.

์ถœ์ฒ˜: ์ด๋ฐ์•„ํ… ์†”๋ฃจ์…˜

API Gateway๋Š” ์‚ฌ์šฉ์ž๊ฐ€ API๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ •๋ฌธ์ด๋‹ค. AWS์—์„œ๋Š” ์ด๋Ÿฌํ•œ API๋ฅผ ์‰ฝ๊ฒŒ ์ƒ์„ฑ ๋ฐ ๋ฐฐํฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์ด๋Ÿฌํ•œ API๋“ค์€ AWS API Gateway Service์—์„œ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ API Gateway Service๋ฅผ ์ด์šฉํ•ด Lambda๋ฅผ ํ˜ธ์ถœํ•˜๊ฑฐ๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€์˜ ์—ฐ๋™ ๋“ฑ AWS ์—ฌ๋Ÿฌ ๋‹ค๋ฅธ ์„œ๋น„์Šค์™€ ์—ฐ๋™ํ•  ์ˆ˜ ์žˆ๋‹ค.

6.2 Create API Gateway and Integration Lambda

API Gateway ์—์„œ API๋ฅผ ์ƒ์„ฑํ•œ๋’ค ์ƒ์„ฑ๋œ API์™€ Lambda๋ฅผ ์—ฐ๊ฒฐํ•ด API๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ๋งˆ๋‹ค Lambda๋ฅผ ํŠธ๋ฆฌ๊ฑฐ๋งํ•˜๋„๋ก ์—ฐ๋™์‹œ์ผœ ๋ณด์ž

AWS ์ฝ˜์†” ์„œ๋น„์Šค์—์„œ API Gateway๋กœ ์ ‘์†ํ•œ ๋’ค ์ขŒ์ธก ํŒจ๋„ API๋ฅผ ํด๋ฆญํ•œ ๋’ค API๋ฅผ ์ƒ์„ฑํ•˜์ž. ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ์•„๋ž˜ 4๊ฐ€์ง€ ์œ ํ˜•์ด ์žˆ๋‹ค.

  1. HTTP
  2. Websocket
  3. REST
  4. REST(Private)

์—ฌ๊ธฐ์„œ HTTP API ํ˜น์€ REST API ๋‘ API๋Š” ๊ฑฐ์˜ ๋น„์Šทํ•œ ์ผ์„ ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ HTTP API๋Š” ๋‹จ์ˆœํ•œ ์ž‘์—… ๋ฐ–์— ๋ชปํ•˜์ง€๋งŒ ๊ฐ€๊ฒฉ๋ฉด์—์„œ ์ €๋ ดํ•˜๋‹ค. ๋ฐ˜๋ฉด REST API๋Š” ๋ณต์žกํ•œ ์ž‘์—…๊ณผ ์—ฐ๋™์ด ๋˜์ง€๋งŒ ๊ฐ€๊ฒฉ์ด ๋น„์‹ธ๋‹ค. ๋‘ API ๋ชจ๋‘ Lambda์™€ ์—ฐ๋™์ด ๋˜์ง€๋งŒ ๋‚˜์ค‘์—๋Š” GET์„ ํ†ตํ•ด S3์— ์žˆ๋Š” ์ •์  HTML์„ ๋ถˆ๋Ÿฌ์™€์•ผ ํ•˜๋ฏ€๋กœ REST API๋กœ ์ƒ์„ฑํ•œ๋‹ค.

๋ฐฉ๊ธˆ ๋งŒ๋“  IoT_Lamp_API ์ขŒ์ธก ํƒญ์—์„œ ๋ฆฌ์†Œ์Šค์—์„œ ์ƒˆ๋กœ์šด ๋ฆฌ์†Œ์Šค๋ฅผ ์ƒ์„ฑํ•ด์•ผํ•œ๋‹ค. ๋ฆฌ์†Œ์Šค ์ด๋ฆ„์€ shadow-state๋กœ ์„ค์ •ํ•œ ๋’ค API Gateway CORS ํ™œ์„ฑํ™” ์—ฌ๋ถ€์— ์ฒดํฌ๋ฅผ ํ•ด์ค€๋‹ค.

์ƒˆ๋กœ ์ƒ์„ฑํ•œ ๋ฆฌ์†Œ์Šค์ธ shdow-state ์•„๋ž˜์— ์ƒˆ๋กœ์šด GET ๊ณผ POST ๋ฉ”์†Œ๋“œ๋ฅผ ๋งŒ๋“ค์–ด Lambda์™€ ๋งคํ•‘์‹œํ‚ค๋„๋ก ํ•œ๋‹ค.

์ด์ œ ์ž‘์—…๋ฒ„ํŠผ์„ ํด๋ฆญํ•ด ๋ฐฐํฌํ•œ๋‹ค. ๋งŒ์•ฝ ๋ฐฐํฌ์Šคํ…Œ์ด์ง€๊ฐ€ ์—†๋‹ค๋ฉด ์ƒˆ ์Šคํ…Œ์ด์ง€์—์„œ Dev ๋ฐฐํฌ ์Šคํ…Œ์ด์ง€๋ฅผ ๋งŒ๋“ค์–ด ๋ฐฐํฌํ•œ๋‹ค.

7. Create S3 Bucket and upload static HTML

AWS S3๋Š” Simple Stroage Service ์˜ ์•ฝ์ž๋กœ AWS ์—์„œ ์ œ๊ณตํ•˜๋Š” Cloud Stroage ์ด๋‹ค. AWS S3์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” bucket์„ ์ƒ์„ฑํ•ด์•ผํ•˜๋Š”๋ฐ AWS ์—์„œ bucket์ด๋ž€ ๋‹ค์Œ์„ ์ง€์นญํ•œ๋‹ค.

Amazon S3์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋ ค๋ฉด ๋ฒ„ํ‚ท ๋ฐ ๊ฐ์ฒด๋ผ๋Š” ๋ฆฌ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋ฒ„ํ‚ท์€ ๊ฐ์ฒด์— ๋Œ€ํ•œ ์ปจํ…Œ์ด๋„ˆ์ž…๋‹ˆ๋‹ค. ๊ฐ์ฒด๋Š” ํŒŒ์ผ๊ณผ ํ•ด๋‹น ํŒŒ์ผ์„ ์„ค๋ช…ํ•˜๋Š” ๋ชจ๋“  ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์ž…๋‹ˆ๋‹ค.

Amazon S3์— ๊ฐ์ฒด๋ฅผ ์ €์žฅํ•˜๋ ค๋ฉด ๋ฒ„ํ‚ท์„ ์ƒ์„ฑํ•œ ๋‹ค์Œ ๋ฒ„ํ‚ท์— ๊ฐ์ฒด๋ฅผ ์—…๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค. ๊ฐ์ฒด๊ฐ€ ๋ฒ„ํ‚ท์— ์žˆ์œผ๋ฉด ๊ฐ์ฒด๋ฅผ ์—ด๊ณ  ๋‹ค์šด๋กœ๋“œํ•˜๊ณ  ์ด๋™ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ์ฒด๋‚˜ ๋ฒ„ํ‚ท์ด ๋” ์ด์ƒ ํ•„์š”ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๋ฆฌ์†Œ์Šค๋ฅผ ์ •๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ถœ์ฒ˜: AmazonS3 UserGuide - Working with buckets

AWS ๊ด€๋ฆฌ ์ฝ˜์†”์—์„œ S3์— ์ ‘์† ํ›„ ๋ฒ„ํ‚ท ํƒญ์œผ๋กœ ๋“ค์–ด๊ฐ€ ์ƒˆ๋กœ์šด bucket์„ ๋งŒ๋“ค์–ด ๋ณด์ž. bucket์— ์ด๋ฆ„์„ ์ •ํ• ๋•Œ๋Š” ๋ช‡ ๊ฐ€์ง€ ๊ทœ์น™์ด ์กด์žฌํ•˜๋Š”๋ฐ ์†Œ๋ฌธ์ž๋งŒ ๊ฐ€๋Šฅํ•˜๊ณ  ํŠน์ˆ˜๋ฌธ์ž๋Š” dot(.) ๊ณผ hyphens(-) ๋งŒ ์ง€์›ํ•˜๊ณ  ๋˜ URL ํ˜•์‹์€ ๊ถŒ์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์ด ๊ถ๊ธˆํ•˜๋‹ค๋ฉด ๐Ÿ”— AWS S3 Bucket naming rules ์— ์ ‘์†ํ•ด ํ™•์ธํ•ด ๋ณด์ž.

์ด๋ฆ„์„ ์ •ํ–ˆ๋‹ค๋ฉด ๊ทธ ์™ธ ๋ชจ๋“  ์˜ต์…˜์€ ๋””ํดํŠธ๋กœ ์„ ํƒํ•œ ๋’ค ์ƒ์„ฑํ•˜๋ฉด ๋œ๋‹ค. bucket์„ ์ƒ์„ฑํ–ˆ์œผ๋‹ˆ ์•„๋ž˜ ์ฝ”๋“œ๋กœ HTMLํŒŒ์ผ์„ ํ•˜๋‚˜ ๋งŒ๋“ ๋‹ค.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>IoT Lamp</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.5/css/bulma.min.css">
    <script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
    <script type = "text/javascript"
       src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js">
    </script>
 
    <script type="text/javascript">
    $(window).load(function() {
    });
 
      $(document).ready(function() {
         $("#onoff").click(function(event){
 
            currentvalue = document.getElementById('onoff').innerHTML;
            if(currentvalue == "It's Off"){
                    // trying to add the value from text box 
                    thingName = document.getElementById("txtField").value;
                    console.log(thingName);
                    var mydata={"action":"on", "thingname":thingName};
                    console.log(mydata);
                    document.getElementById("onoff").innerHTML="It's On";
                    document.getElementById("onoff").setAttribute("class","button is-rounded is-large is-fullwidth is-loading");
                      $.ajax( {
                        type: 'POST',
                         url:'',
                         contentType: 'application/json',
                         crossDomain: true,
                         processData: false,
                         dataType: "json",
                         data: JSON.stringify(mydata),
                         success:function(data) {
                           console.log(" on success");
                            document.getElementById("onoff").setAttribute("class","button is-success is-rounded is-large is-fullwidth");
                         },
                         error:function(data) {
                           console.log(" on fail;");
                            document.getElementById("onoff").setAttribute("class","button is-success is-rounded is-large is-fullwidth");
                         }
                      });
            }
            else {
                document.getElementById("onoff").innerHTML="It's Off";
                // trying to add the value from text box 
                thingName = document.getElementById("txtField").value;
                console.log(thingName);
                var mydata={"action":"off", "thingname":thingName};
                console.log(mydata);
                document.getElementById("onoff").innerHTML="It's Off";
                document.getElementById("onoff").setAttribute("class","button is-rounded is-large is-fullwidth is-loading");
                  $.ajax( {
                    type: 'POST',
                     url:'',
                     contentType: 'application/json',
                     crossDomain: true,
                     processData: false,
                     dataType: "json",
                     data: JSON.stringify(mydata),
                     success:function(data) {
                       console.log(" off success");
                        document.getElementById("onoff").setAttribute("class","button is-danger is-rounded is-large is-fullwidth");
                     },
                     error:function(data) {
                       console.log(" off fail");
                        document.getElementById("onoff").setAttribute("class","button is-danger is-rounded is-large is-fullwidth");
                     }
                  });
            }
         });
 
         $("#fndbtn").click(function(event){
            console.log("testing the button");
            //check the status of Shadow
            // thingName = "ESP32"
            thingName = document.getElementById("txtField").value;
            console.log(`thingname=${thingName}`);
            $.ajax( {
              type: 'GET',
               url:'',
               contentType: 'application/json',
               crossDomain: true,
               processData: false,
               dataType: "json",
               data: `thingname=${thingName}`,
               success:function(data) {
                console.log("success");
                console.log(data.status);
                document.getElementById("onoff").innerHTML=data.status;
                if (data.status == "It's On" )
                {
                  document.getElementById("onoff").setAttribute("class","button is-success is-rounded is-large is-fullwidth");
                }
                else{
            
                  document.getElementById("onoff").setAttribute("class","button is-danger is-rounded is-large is-fullwidth");
                }
               },
               error:function(data) {
                 console.log("error");
               }
            });
        });
      });
 
</script>
  </head>
  <body>
    <div class="columns is-mobile">
    <div class="column is-2">
    <!-- First Column-->
    </div>
    <div class="column is-8">
      <br><br><br><br><br>
      <h1 class="title is-1" style="text-align: center;">IoT Lamp</h1>
      <br><br>
      <center>
        <p>Enter Thing Name: </p>
        &nbsp
      </center>
      <center>
        <input name="txtField" type="text" maxlength="512" id="txtField" class="searchField" style="padding: 8px;" />
        &nbsp
        <!-- <input type = "submit" id = "fndbtn" value = "Submit"/> -->
        <a class="button" id = "fndbtn">Check</a>
      </center>
      <br><br>
      <a class="button is-info is-rounded is-large is-fullwidth" id="onoff" value="on">Pending Check</a>
 
    </div>
    <div class="column is-2">
    <!-- Third Column-->
    </div>
  </div>
  </body>
</html>

์œ„ HTML ์ฝ”๋“œ์— 31, 58, 84๋ฒˆ ๋ผ์ธ์˜ url ๋ถ€๋ถ„์ด ๊ณต๋ž€์ธ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ์—ฌ๊ธฐ์— ์•„๊นŒ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“ค์—ˆ๋˜ POST ์™€ GET์˜ url์„ ๋„ฃ์–ด์ฃผ๋ฉด ๋œ๋‹ค. url ํ™•์ธ์€ API Gateway ์ฝ˜์†”์—์„œ IoT_Lamp_API ํƒญ์œผ๋กœ ๋“ค์–ด๊ฐ„ ๋’ค, ์Šคํ…Œ์ด์ง€ ์˜์—ญ์„ ํด๋ฆญํ•˜๋ฉด ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

8. AWS API Gateway S3 Integration

์ด์ œ AWS API Gateway์™€ S3๋ฅผ ์—ฐ๋™ํ•ด๋ณด์ž. ์ด๋ฒˆ ์„น์…˜์ด ๋๋‚˜๋ฉด ๋ธŒ๋ผ์šฐ์ € ์ฐฝ์— AWS Gateway์—์„œ ๋งŒ๋“  AWS IoT Lamp API์˜ API๋ฅผ ํ†ตํ•ด ์‹ค์ œ Lamp๋ฅผ ์ œ์–ดํ•˜๋Š” Web์œผ๋กœ ์ ‘์†ํ•  ์ˆ˜ ์žˆ๋‹ค.

์šฐ์„  API Gateway๊ฐ€ S3์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” Role์„ ๋งŒ๋“ค์–ด ๋ณด์ž IAM ์ฝ˜์†”์— ์ ‘์†ํ•œ ๋’ค ์ขŒ์ธก ํŒจ๋„ ์•ก์„ธ์Šค ๊ด€๋ฆฌ โžœ ์—ญํ•  ์— ์ ‘์†ํ•ด ์—ญํ•  ๋งŒ๋“ค๊ธฐ ๋ฅผ ํด๋ฆญํ•ด ์ƒˆ๋กœ์šด ์—ญํ• ์„ ๋งŒ๋“ค์ž.

์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ์œ ํ˜•์˜ ๊ฐœ์ฒด ์„ ํƒ ๋ž€์—์„œ AWS ์„œ๋น„์Šค ์™€ ๋˜๋Š” ์„œ๋น„์Šค๋ฅผ ์„ ํƒํ•˜์—ฌ ํ•ด๋‹น ์„œ๋น„์Šค์˜ ์‚ฌ์šฉ ์‚ฌ๋ก€ ํ™•์ธ ๋ž€์—์„œ API Gateway๋ฅผ ์„ ํƒํ•˜์ž ๋ชจ๋“  ์Šคํ…Œ์ด์ง€๋ฅผ ๋””ํดํŠธ๋กœ ์„ค์ •ํ•œ ๋’ค ์—ญํ•  ์ด๋ฆ„์„ ์ •ํ•œ๋‹ค. ์—ญํ•  ์ด๋ฆ„์€ APIGatewayAccessS3๋กœ ์„ค์ •ํ•œ๋‹ค.

์—ญํ• ์„ ๋งŒ๋“ค์—ˆ์œผ๋‹ˆ ์ด ์—ญํ• ๊ณผ ์—ฐ๊ฒฐ๋  ์ •์ฑ…์„ ์ƒ์„ฑํ•œ๋‹ค. ์•ก์„ธ์Šค ๊ด€๋ฆฌ โžœ ์ •์ฑ… ์— ์ ‘์†ํ•ด ์ •์ฑ… ์ƒ์„ฑ์„ ํด๋ฆญํ•œ๋’ค JSON ํƒญ์— ๋“ค์–ด๊ฐ€ ์•„๋ž˜ JSON ๋ฌธ์„œ๋ฅผ ๋ถ™์—ฌ ๋„ฃ๊ธฐํ•œ๋‹ค.

{
   "Version":"2012-10-17",
   "Statement":[
      {
         "Effect":"Allow",
         "Action":[
            "s3:GetObject"
         ],
         "Resource":"arn:aws:s3:::<bucket_name>/<object_name>"
      }
   ]
}

<bucket_name>/<object_name> ์€ ๊ฐ์ž์˜ bucket๊ณผ bucket์•ˆ์— HTML ํŒŒ์ผ์„ ๋„ฃ์–ด์ฃผ๋ฉด๋œ๋‹ค. ์ด๋ฒˆ ์‹ค์Šต์„ ์ด๋ฆ„๊นŒ์ง€ ๋˜‘๊ฐ™์ด ๋”ฐ๋ผํ–ˆ๋‹ค๋ฉด iot-lamp-bucket/IoT_Lamp.html ์„ ์ ์–ด์ฃผ๋ฉด ๋œ๋‹ค. ์ •์ฑ… ์ด๋ฆ„์€ IoTLampBucketAccessPolicy ๋กœ ์ •ํ–ˆ๋‹ค.

๋‹ค์‹œ ์—ญํ•  ํƒญ์œผ๋กœ ๋“ค์–ด๊ฐ€ APIGatewayAccessS3์— ๊ถŒํ•œํƒญ์—์„œ ๋ฐฉ๊ธˆ๋งŒ๋“  IoTLampBucketAccessPolicy ์ •์ฑ…์„ ์—ฐ๊ฒฐํ•œ๋‹ค.

์ด์ œ API Gateway์˜ IoT_Lamp_API์˜ ๋ฃจํŠธ ๋ฆฌ์†Œ์Šค์—์„œ ์ƒˆ๋กœ์šด GET ๋ฉ”์†Œ๋“œ๋ฅผ ์ƒ์„ฑํ•˜์ž.

๋ฉ”์†Œ๋“œ๋ฅผ ์ƒ์„ฑํ•œ ๋’ค ์ขŒ์ธก ํƒญ์—์„œ ์„ค์ •๋ž€์œผ๋กœ ๋“ค์–ด๊ฐ€ ์ด์ง„ ๋ฏธ๋””์–ด ํ˜•์‹ ์— ์ด์ง„ ๋ฏธ๋””์–ด ํ˜•์‹ ์ถ”๊ฐ€ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•œ๋‹ค.

์„ค์ •์„ ๋งˆ์น˜๋ฉด ๋‹ค์‹œ ๋ฐฐํฌ๋ฅผ ๋ˆŒ๋Ÿฌ API๋ฅผ ๋ฐฐํฌํ•œ๋’ค ์›น ๋ธŒ๋ผ์šฐ์ ธ์—์„œ ๋ฃจํŠธ ๋ฆฌ์†Œ์Šค๋กœ ์ ‘์†์„ ์‹œ๋„ํ•ด๋ณด์ž.

9. Test

Enter Thing Name์— ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ์‚ฌ๋ฌผ ์ด๋ฆ„์ธ ESP32๋ฅผ ์ ๊ณ  Pending Check ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅธ ํ›„ on/off ๋ฅผ ํ•จ์—๋”ฐ๋ผ Lamp ์—ญ์‹œ on/off๊ฐ€ ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๋””๋ฐ”์ด์Šค ์„€๋„์šฐ ์ƒํƒœ ๋ฌธ์„œ๋ฅผ ํ™•์ธํ•˜๋ฉด desired ์™€ reported์— status ๋ผ๋Š” ์†์„ฑ์ด ์ถ”๊ฐ€๋„์žˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜์žˆ๋‹ค.

์ด๋ฐ–์— CloudWatchlog ๋‚˜ Mqtt Test๋ฅผ ์ด์šฉํ•ด Lambda๋ฅผ ํŠธ๋ฆฌ๊ฑฐ๋ง์ด ์ž˜ ๋˜๋Š”๊ฒƒ ์—ญ์‹œ ํ™•์ธ ๊ฐ€๋Šฅํ•˜๋‹ค.

๐Ÿ”— Connecting AWS with ESP32 Create an IoT Lamp

0๊ฐœ์˜ ๋Œ“๊ธ€