현관문 프로젝트) LED Strip + Door sensor + esp8266 & pi 6편 (AWS IoT)

최재혁·2023년 3월 6일

드디어 끝이 보입니다.

지금까지 진행한 일...

  1. 엔드 디바이스에서 신호를 보내면, 라즈베리파이에서 zigbee 데이터를 받고 AWS IoT로 넘겨줍니다.

  2. WLED가 설치된 ESP8266을 포트포워딩을 통해 외부IP로 접속할 수 있게 설정해 주었습니다.

그럼 이제 무엇을 해야하느냐!

EC2에 node-red를 구성해서 로직을 짜주어야 합니다!!

1. EC2 생성

EC2는 우분투 20.04로 생성하고 Public subnet에 구성해줍니다.

private subnet에 구성하는 것은 테스트를 우선 진행하고 완성한 후 이미지를 따서 넘길 생각입니다.

2. node-red 설치

ec2에 ssh 접속을 통해 node-red를 설치해 줍시다.

   curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
   sudo apt-get install -y nodejs build-essential
   sudo npm install -g --unsafe-perm node-red
   sudo npm install -g --unsafe-perm pm2
   pm2 start `which node-red` -- -v
   pm2 save
   pm2 startup

3. 보안그룹 (수정필요!!!)

테스트할 것이기 때문에 해당 포트에 대해 0.0.0.0/0 으로 설정했습니다.

이부분은 보안상 좋지 못한 예시입니다.

로직만 구성한 후 재정리를 하도록 하겠습니다.

4. node-red 플로우

(잘만 동작하면 되지않을까..?)

딱봐도 복잡해 보입니다.
차근 차근 살펴봅시다.

4-1. mqtt in

  1. 연필 표시를 눌러줍니다.

  2. AWS IoT엔드포인트 및 포트를 입력해 줍니다. (포트는 8883)

  3. 사용TLS에서 연필 부분을 눌러줍니다.

    라즈베리파이에 넣어놨던 인증서, private key, CA를 넣어줍니다.

  4. 확인을 누르면 연결이 완료됩니다.

4-2. function

DO : door open
DF : door close
MO : motion on
MF : motion off

//DO
var msg2 = { payload: {} };

if (msg.payload.contact === false) {
    return msg2
}
//DF, MF
var msg8 = { payload: {} };

if (msg.payload.occupancy === false) {
    return msg8;
}
//MO
var msg3 = { payload: {} };
//msg.payload.contact === true && 
if (msg.payload.occupancy === true) {
    return msg3;
}
//DO,MO
var msg7 = { payload: {} };
//msg.payload.contact === false && 
if (msg.payload.contact === false && msg.payload.occupancy === true) {
    return msg7;
}

여기서 trigger을 추가해줍니다.

이유는 door sensor가 상태값을 가지지 않기 때문에 고정시켜줌으로써 동작 안정화를 위해 달아주었습니다.

4-3. Door Open

4-3-1. 문이 열렸을 때

4-3-2. change

{"on":true,"bri":128,"transition":7,"mainseg":0,"seg":[{"id":0,"start":0,"stop":200,"grp":1,"spc":0,"of":0,"on":true,"frz":false,"bri":248,"cct":127,"col":[[255,200,0],[255,224,160],[255,255,0]],"fx":19,"sx":95,"ix":181,"pal":3,"sel":true,"rev":true,"mi":true},{"stop":0},{"stop":0},{"stop":0},{"stop":0},{"stop":0},{"stop":0},{"stop":0},{"stop":0},{"stop":0},{"stop":0},{"stop":0},{"stop":0},{"stop":0},{"stop":0},{"stop":0}]}

API 형식으로 HTTP WLED에 request를 날려줍니다.

4-3-3. http request

4-4. Motion On

4-4-1. openweathermap 추가하기

오른쪽 위 배포하기 오른쪽에 줄3개 클릭

팔렛트 관리 클릭

설치가능한노드 에서 openweathermap 검색 후 설치

WLED 또한 설치

4-4-2. openweathermap 설정

openweathermap 에서 얻은 api key와 위도, 경도 추가

4-4-3. 0~50개의 LED는 날씨를 표시

//0~50개 weather 표시

var weather = msg.payload.weather;
var temperature = msg.payload.tempc;
var msg4 = { payload: {} };
msg4.payload.on = true
msg4.payload.bri = 128
msg4.payload.transition = 7
msg4.payload.mainseg = 0



// Set the color and effect of the first LED segment based on the weather
switch (weather) {
    case "Clear":
        msg4.payload.seg =
            [{
                "id": 0, "start": 0, "stop": 50, "grp": 1, "spc": 0, "of": 0, "on": true, "frz": false,
                "col": [[3, 169, 244], [3, 169, 244], [3, 169, 244]],
                "fx": 12,
                "sx": 95,
                "ix": 181,
                "pal": 2,
                "sel": false,
                "rev": true,
                "mi": false
            }];
        break;
    case "Clouds":
        msg4.payload.seg =
            [{
                "id": 0, "start": 0, "stop": 50, "grp": 1, "spc": 0, "of": 0, "on": true, "frz": false,
                "col": [[189, 189, 189], [189, 180, 189], [189, 200, 189]],
                "fx": 12,
                "sx": 95,
                "ix": 181,
                "pal": 2,
                "sel": false,
                "rev": true,
                "mi": false
            }];
        break;
    case "Rain":
        [{
            "id": 0, "start": 0, "stop": 50, "grp": 1, "spc": 0, "of": 0, "on": true, "frz": false,
            "col": [[33, 150, 243], [33, 150, 243], [33, 150, 243]],
            "fx": 12,
            "sx": 95,
            "ix": 181,
            "pal": 2,
            "sel": false,
            "rev": true,
            "mi": false
        }];
        break;
    case "Snow":
        [{
            "id": 0, "start": 0, "stop": 50, "grp": 1, "spc": 0, "of": 0, "on": true, "frz": false,
            "col": [[255, 245, 254], [255, 245, 254], [255, 245, 254]],
            "fx": 12,
            "sx": 95,
            "ix": 181,
            "pal": 2,
            "sel": false,
            "rev": true,
            "mi": false
        }];
        break;
    default:
        msg4.payload.seg =
            [{
                "id": 0, "start": 0, "stop": 60, "grp": 1, "spc": 0, "of": 0, "on": true, "frz": false,
                "col": [[128, 128, 128], [128, 128, 128], [128, 128, 128]],
                "fx": 12,
                "sx": 95,
                "ix": 181,
                "pal": 2,
                "sel": false,
                "rev": true,
                "mi": false
            }];
}



return msg4;
//날씨 컬러

Clear: #03A9F4 [3, 169, 244] (파란색)
Clouds: #BDBDBD [189, 189, 189] (회색)
Rain: #2196F3 [33, 150, 243] (진한 파란색)
Snow: #E1F5FE [225, 245, 254](연한 파란색)

잘 보시면 col의 배열이 3개입니다. 기준이 되는 색상 1개와 비슷한 계열의 색상 2개를 추가함으로써 밋밋한 효과를 줄일 수 있습니다.

4-4-4. 51~100개의 LED는 온도를 표시

//51 ~ 100개의 LED는 온도 표시

var weather = msg.payload.weather;
var temperature = msg.payload.tempc;
var msg6 = { payload: {} };

msg6.payload.bri = 128
msg6.payload.transition = 7
msg6.payload.mainseg = 0


// Set the color and effect of the second LED segment based on the temperature
if (temperature <= 0) {
    msg6.payload.seg =
        [{
            "id": 1, "start": 51, "stop": 100, "grp": 1, "spc": 0, "of": 0, "on": true, "frz": false,
            "col": [[135, 206, 250], [150, 206, 250], [170, 206, 250]],
            "fx": 12,
            "sx": 95,
            "ix": 181,
            "pal": 2,
            "sel": false,
            "rev": true,
            "mi": false
        }];
} else if (temperature > 0 && temperature <= 5) {
    msg6.payload.seg =
    [{
        "id": 1, "start": 51, "stop": 100, "grp": 1, "spc": 0, "of": 0, "on": true, "frz": false,
        "col": [[0, 255, 255], [100, 255, 255], [200, 255, 255]],
        "fx": 12,
        "sx": 95,
        "ix": 181,
        "pal": 2,
        "sel": false,
        "rev": true,
        "mi": false
    }];
} else if (temperature > 5 && temperature <= 10) {
    msg6.payload.seg =
        [{
            "id": 1, "start": 51, "stop": 100, "grp": 1, "spc": 0, "of": 0, "on": true, "frz": false,
            "col": [[154, 205, 50], [154, 205, 100], [154, 205, 150]],
            "fx": 12,
            "sx": 95,
            "ix": 181,
            "pal": 2,
            "sel": false,
            "rev": true,
            "mi": false
        }];
} 
else if (temperature > 10 && temperature <= 15) {
    msg6.payload.seg =
        [{
            "id": 1, "start": 51, "stop": 100, "grp": 1, "spc": 0, "of": 0, "on": true, "frz": false,
            "col": [[255, 255, 0], [255, 255, 100], [255, 255, 200]],
            "fx": 12,
            "sx": 95,
            "ix": 181,
            "pal": 2,
            "sel": false,
            "rev": true,
            "mi": false
        }];
}
else if (temperature > 15 && temperature <= 20) {
    msg6.payload.seg =
        [{
            "id": 1, "start": 51, "stop": 100, "grp": 1, "spc": 0, "of": 0, "on": true, "frz": false,
            "col": [[255, 165, 0], [255, 165, 100], [255, 165, 150]],
            "fx": 12,
            "sx": 95,
            "ix": 181,
            "pal": 2,
            "sel": false,
            "rev": true,
            "mi": false
        }];
}
else if (temperature > 20 && temperature <= 25) {
    msg6.payload.seg =
        [{
            "id": 1, "start": 51, "stop": 100, "grp": 1, "spc": 0, "of": 0, "on": true, "frz": false,
            "col": [[255, 0, 0], [255, 0, 50], [255, 0, 100]],
            "fx": 12,
            "sx": 95,
            "ix": 181,
            "pal": 2,
            "sel": false,
            "rev": true,
            "mi": false
        }];
}
else {
    msg6.payload.seg =
        [{
            "id": 1, "start": 51, "stop": 100, "grp": 1, "spc": 0, "of": 0, "on": true, "frz": false,
            "col": [[255, 0, 255], [255, 100, 255], [255, 200, 255]],
            "fx": 12,
            "sx": 95,
            "ix": 181,
            "pal": 2,
            "sel": false,
            "rev": true,
            "mi": false
        }];
}
return msg6;
//온도에 따른 컬러 값

하늘색: (135, 206, 250) 0도 이하
연녹색 (0, 255, 255) 0도 이상 5도 이하 
연두색: (154, 205, 50) 5도이상 10도이하 
노란색: (255, 255, 0) 10도이상 15도 이하 
주황색: (255, 165, 0) 15도 이상 20도 이하
빨간색: (255, 0, 0) 20도 이상 25이하
자홍색: (255, 0, 255) 25도 이상 30도 이하

4-4-5. 기타

1,2번 WLED는 OFF
3,4번 WLED는 ON

모션센서의 탐지 시간은 트리거된 후 1분이 소요되므로 모션센서에서 보여주는 LED는 1분으로 설정했습니다.

5. 왜 이딴 형식인가?

  • 모션센서가 ON 됬을 때 LED strip을 반으로 갈라서 보여주려하니 도어센서 seg 설정과 겹치는 문제가 발생되었다.
  • 즉, seg0을 1~50, seg1을 51~100으로 지정하고, seg2로 0~100으로 지정이 안되었다.
  • 따라서, 도어센서가 반응할 때는 seg0을 강제로 0~100으로 만들고, 모션센서가 반응할 때는 seg 0~50, seg1 51~100을 지정해주었다.
  • 무수한 시도끝에 이렇게 설정할 수 밖에 없었다...ㅠㅠ
profile
한다면 하는 남자! 줄여서 한남!

0개의 댓글