iot

4 minute read

iot


iot수업을 수강하면서 창문 자동 개폐 장치를 만들었다.
작동 원리는 이와 같다.

  1. arduino의 센서를 통해 온도와 습도를 측정하고 그 데이터를 aws iot core의 topic으로 publish하면 사전에 설정한 iot core의 rule을 통해서 aws s3에 저장한다.
  2. ✅ aws lambda의 트리거를 s3의 데이터 저장으로 설정하여 aws lambda 함수를 작동시킨다.
  3. ✅ aws lambda 함수는 사용자의 위치를 파악하여 해당 위치의 온도와 습도를 크롤링하는 함수이다.
  4. ✅ 해당 lmabda 함수의 destination으로 또 다른 lanbda 함수를 설정해놓는다.
  5. ✅ 두번째 lambda 함수는 s3에 저장한 온도, 습도 데이터와 크롤링함수를 통해 찾은 사용자 지역의 온도 및 습도를 비교하여 권고기준이 넘어가면 google fcm 을 통해서 android로 알림을 보내는 함수이다.
  6. android에서 사용자가 이벤트(open & close)를 발생시켜서 해당 데이터("open" or "close")를 s3에 저장하면
  7. s3의 데이터 저장 과 trigger로 연결된 또 다른 lambda함수를 통해 arduino의 endpoint로 사용자 명령을 전송한다.
  8. arduino에서 받은 데이터를 가지고 모터를 작동시켜 문을 열고 닫는다.
✅ : my task



1. lambda code - crawling-region
import json 
import boto3 
import botocore
### 위에는 s3 연결 패키지 아래는 크롤링 패키지
import requests
from bs4 import BeautifulSoup

source = requests.get('https://www.weather.go.kr/weather/observation/currentweather.jsp')
soup = BeautifulSoup(source.content,"html.parser")

table = soup.find('table',{'class':'table_develop3'})
data = []

## s3 버킷 저장
BUCKET_NAME = 'all-region-data'
KEY = 'all_location_data.txt' 
s3_client = boto3.client('s3') 

s3 = boto3.resource('s3')
    
def lambda_handler(event, context):
    s3.Object(BUCKET_NAME, KEY).delete()
    print("지역     온도     습도")
    for tr in table.find_all('tr'):
        tds = list(tr.find_all('td'))
        for td in tds:
            if td.find('a'):
                point = td.find('a').text
                temp = tds[5].text
                humidity = tds[10].text
                print("{0:<7} {1:<7} {2:<7}".format(point,temp,humidity))
                data.append([point,temp,humidity])
             
    ## 버킷에 저장하는 코드  
    s3_client.put_object(Body=json.dumps(data, ensure_ascii=False), Bucket=BUCKET_NAME, Key=KEY)  


2. lambda code - SendToAndroid
const http = require('https');
const AWS = require('aws-sdk');
const s3 = new AWS.S3();
const src_bkt = 'sensor-esp32'
const src_key = 'location_data.txt'
const src_key2 = 'text.txt' // bme280에서 측정하는 값(온도, 습도)


const src_bkt_crawl = 'all-region-data'
const src_key_crawl = 'all_location_data.txt'

const src_key_time = 'alarm_current_time.txt' // 알림을 보내는 시간을 저장하는 txt파일

let outside_temp = false
let outside_humid = false

let date = new Date();
let hours = date.getHours()
let minutes = date.getMinutes();  // 현재 분
let time = hours + " " + minutes
let alarm_or_not = false // 알람 보낼지 말지 여부


exports.handler = async(event, context) => {
    
    console.log(time)
    var params = { 
        Bucket: src_bkt,
        Key: src_key
    };
    
    var params2 = { 
        Bucket: src_bkt,
        Key: src_key2
    };
    
    var params3 = { 
        Bucket: src_bkt_crawl,
        Key: src_key_crawl
    };

    var params4 = {  // 알림 보낼 시간 저장하기 위한 파람 --> 현재시간을 저장한다 
        Bucket: src_bkt,
        Key: src_key_time,
        Body: time
    };

    
    var params5 = {  // 알림 보냈던 시간 불러오기 위한 파람
        Bucket: src_bkt,
        Key: src_key_time
    };

    
    const data = await s3.getObject(params).promise();
    const data2 = await s3.getObject(params2).promise();
    const data3 = await s3.getObject(params3).promise(); // 크롤링 파일에서 가져온 데이터
    const data5 = await s3.getObject(params5).promise(); 
    
    
    const response = {
        statusCode: 200,
        body: data.Body
    };
    const response2 = {
        statusCode: 200,
        body2: data2.Body
    };
    
    const response3 = {
        statusCode: 200,
        body3: data3.Body
    };
    
    const response5 = {
        statusCode: 200,
        body5: data5.Body
    };
    
    let alarm_current_hour = new Buffer.from(data5.Body).toString().split(" ")[0]; // 가장 최근에 알림을 보낸 시간
    let alarm_current_minute = new Buffer.from(data5.Body).toString().split(" ")[1]; // 가장 최근에 알림을 보낸 분
    let user_location = new Buffer.from(data.Body).toString().substring(1)
    let local_temp = parseFloat(new Buffer.from(data2.Body).toString().split(',')[0]) // bme에서 측정한 온도 
    let local_humid = parseFloat(new Buffer.from(data2.Body).toString().split(',')[1])
    let crawling_data = new Buffer.from(data3.Body).toString().split(',')
    
    for(var i = 0; i<crawling_data.length; i++){
        if(crawling_data[i].substring(3,5) == user_location) { // 사용자 지정 지역과 크롤링데이터에서 같은 이름의 지역을 찾으면. 
            outside_temp = parseFloat( (crawling_data[i+1]).substring(2, crawling_data[i+1].length-1))
            outside_humid = parseFloat( (crawling_data[i+2]).substring(2, crawling_data[i+2].length-1))
            
        }
    }
    console.log(outside_humid)
    console.log(outside_temp)
    console.log(time)
    console.log(alarm_current_hour)
    console.log(alarm_current_minute)
    // 온도와 습도 시간을 측정하여 조건에 부합하면 s3에 현재시간 저장 
    if( (hours==alarm_current_hour && minutes - 30 > alarm_current_minute) || (hours>alarm_current_hour && minutes+30 > alarm_current_minute) ){ // 현재 시간과 s3에 저장된(마지막으로 알람을 보낸 시간)이 30분이상 차이가 나면 s3에 시간 갱신
        if( (outside_temp > 5 && outside_temp < 24) && (local_temp < 15 || local_temp > 24) ) {
             if( (outside_humid > 40 && outside_humid < 70) && (local_humid < 40 || local_humid > 70)){
                alarm_or_not = true
                const data4 = await s3.putObject(params4).promise(); // 현재 시각을 s3에 저장한다. 
                console.log(11111)
             }
        }
    }
    console.log(alarm_or_not + "----------")
    
//android에 알람보내기
    return new Promise((resolve, reject) => {
      const options = {
          host: "fcm.googleapis.com",
          path: "/fcm/send",
          method: 'POST',
          headers: {
              'Authorization': 'key=AAAA-_cJn0E:APA91bH-WoFFOCgNIxsva_X3byhpNUHWFHu-7anS2mjjgfYRUhkYHpnydVemCIfBz85j3bxFrEwRVvqx0tQb_O5Vl8c06D4miqrei82Hx9XjgM39rNy9JJ09Ou6R0NKXbTvKqJ75r602',
              'Content-Type': 'application/json',
          },
      };
      const title="alert"
      const body="문 여는 것을 권고합니다"
      const req = http.request(options, (res) => {
          resolve('success') ;
      });
       
      req.on('error', (e) => {
          reject(e.message);
      });
       
      const reqBody = '{"to":"' + 'fDGVf22xSdmErsgVeXlQSm:APA91bEJZHu0PFox1k0k2evfu5ALvC_CTQb_J6tLff0UggcSF-U5EQgnwFD3DkILmQUUvODj0ukQQp3rK1ByX9eXZn6mC9yBQYSQoqyn4vnZolKQW3lKdu3Ch0buGrLNqLCAIIpCJBab' + '", "priority": "high", "notification": {"title": "' +title + '", "body": "'+body+'"}}';
      if( alarm_or_not == true ){
          if( (outside_temp > 5 && outside_temp < 24) && (local_temp < 15 || local_temp > 24) ) {
              if( (outside_humid > 40 && outside_humid < 70) && (local_humid < 40 || local_humid > 70)){
                    
                    req.write(reqBody);
                    alarm_or_not=false
              }
          }
      }
      req.end();
    });
    
}


3. lambda code - sendDoorStatusToEsp
import json 
import boto3 
import botocore 
BUCKET_NAME = 'onoff'
KEY = 'door.txt' 
s3_client = boto3.client('s3') 
def lambda_handler(event, context): 
    data = s3_client.get_object(Bucket=BUCKET_NAME, Key=KEY) 
    content = data['Body'].read() 
    
    client = boto3.client('iot-data', region_name='ap-northeast-2')

    # Change topic, qos and payload
    response = client.publish(
        topic = 'iot/lambda/testing',  
        qos=0,
        payload = content
    )
   
    print(content) 
    return { 
        'statusCode': 200, 
        'body': json.dumps(content.decode('UTF-8')) 
        
    }
✋ aws의 여러기능을 써보고 충분히 이해하는데 있어서 정말 중요한 프로젝트였다. aws의 기능은 너무나도 많기 때문에 앞으로 서버공부하는데 더 많은 기능을 써보고 싶다.




시연동영상

Categories:

Updated: