시작하기 전에

IoT 장비 준비

데이터 수집을 위한 IoT 장비를 준비합니다. IoT 장비는 통신에 사용될 MQTT 프로토콜 지원이 가능해야 하며, openssl과 같은 SSL 관련 라이브러리 사용이 가능하고 Java, Python, Node.js 등의 언어를 지원하는 모든 장비에서 사용이 가능합니다.

Cloud IoT Core 서비스 가입

IoT 장비로부터 발생하는 데이터를 Cloud IoT Core에서 수신하기 위해 서비스에 가입합니다. (Cloud IoT Core 서비스의 자세한 설명 및 요금은 포털에서 확인하실 수 있습니다.)

img

  1. All Products > Cloud IoT Core를 선택합니다.
  2. Subscription 메뉴를 통해 화면 중앙의 상품 이용 신청을 선택하면 서비스 이용 신청이 완료됩니다.

메시지 구조 설계

IoT 장비에서 발송할 데이터를 어떤 형태로 보낼 것인지 데이터의 구조 및 토픽(Topic)을 정의합니다. 메시지는 Json 구조를 사용하며, 아래의 예제를 참고하여 메시지 구조를 자유롭게 설계할 수 있습니다.

토픽 : factory/room1/temperature

메시지 구조 :

{
  "deviceId": "device_1",
  "deviceType": "temperature",
  "value": 35,
  "battery": 9,
  "date": "2016-12-15",
  "time": "15:12:00"
}

토픽 및 데이터 구조는 사용자가 원하는 이름 및 구조로 사용할 수 있으며, 일부 시스템에 영향을 주는 특수문자의 경우 제한이 있을 수 있습니다. 자세한 내용은 기타 > 제한 사항을 참고하시기 바랍니다.

인증서 생성하기

IoT 장비와 Cloud IoT Core 서비스 간의 통신은 인증서 기반의 암호화 통신을 사용합니다. Cloud IoT Core에서 발급받을 수 있는 인증서는 인증서, 체인 인증서, 루트 인증서, 프라이빗 키로 총 4가지로 구분됩니다. 해당 인증서를 통해 사용자의 IoT 장비에서 접속하려는 서버가 정상적인 Cloud IoT Core 서버가 맞는지를 확인하고, 서버에서는 정상적으로 인증된 사용자가 맞는지 양방향 인증을 통해 확인하게 됩니다. 상호 간의 인증이 완료되면 보안을 위한 암호화 통신이 진행됩니다. (제공하는 인증서 중 프라이빗 키는 초기 1회만 다운로드 가능하므로 보관에 유의하셔야 합니다.)

인증서 생성

img

  1. 좌측 메뉴에서 Certificates를 선택합니다.
  2. 화면 중앙의 인증서 발급 버튼을 선택합니다.
  3. 인증서 생성 팝업창에서 발급 버튼을 선택합니다.
  4. 인증서 생성에 수 분이 소요될 수 있으며 생성이 완료되면 자동으로 인증서 다운로드 화면으로 이동합니다.
  5. 각각 인증서의 개별 다운로드 버튼을 눌러 인증서, 체인 인증서, 루트 인증서, 프라이빗 키를 내 컴퓨터에 저장합니다.

참고 생성된 인증서의 활성, 비활성, 삭제 등의 기능과 인증서를 가상 디바이스와 연결하여 관리하는 방법은 부가 기능 페이지에서 자세히 확인하실 수 있습니다.

IoT 장비에 인증서 설치

내 컴퓨터에 저장한 인증서는 IoT 장비로 이동하기 위해 안전한 저장 매체를 이용합니다. (IoT 장비에서 웹브라우징이 가능한 경우 직접 다운로드 가능합니다.)

실시간 처리를 위한 규칙(Rules) 생성하기

IoT 장비와 Cloud IoT Core와 연결 준비가 되었다면 다음 단계로 IoT 장비로부터 수신한 데이터를 어떻게 처리할지를 정의하는 규칙(Rules)을 생성해야 합니다. 규칙 생성 단계는 IoT 장비에서 Cloud IoT Core로 보내진 메시지 내용 중 사전 설정된 조건과 일치하는지를 검사하는 "트리거"와, 조건에 부합되면 사전 정의된 동작을 수행하는 "액션"으로 구성되어 있습니다. 예를 들면, "메시지에 온도 값이 80도 이상인 경우(트리거), 사용자에게 Push 알림을 전송(액션)"과 같은 규칙을 생성할 수 있습니다. 트리거와 액션은 최소 1개씩은 정의되어 있어야 최종적으로 규칙 생성이 가능합니다.

트리거 생성

IoT 장비에서 송신되는 메시지 내용에 대해 특정한 트리거 조건을 생성합니다.

  1. 좌측 메뉴에서 Rules를 선택합니다.

  2. 화면 중앙의 규칙 생성 버튼을 선택합니다.

    img

  3. 트리거 생성 화면에서 트리거 이름(사용자가 구분할 수 있는 이름), 트리거 설명(트리거의 상세 설명), 트리거 쿼리(메시지를 검사할 수 있는 조건)를 입력합니다.

  4. "쿼리검증" 버튼을 선택하면 입력한 트리거 쿼리에 대한 문법 검사를 수행해 적합한 경우 트리거 생성 하단에 액션 생성 화면이 나타납니다.

트리거 쿼리 작성 규칙

트리거를 생성하기 위해서는 SQL 구문을 이해하고 있어야 하며 트리거 쿼리 작성 규칙은 다음과 같습니다.

Select [메시지의 Key1, Key2, ...] From `[토픽]` Where [조건]

상단의 메시지 구조 설계에서 정의한 메시지 구조를 예로 battery 값이 10%보다 적게 남은 경우를 검사하고 싶다면 아래와 같이 쿼리문을 작성합니다.

Select * From `factory/room1/temperature` Where battery < 10

여기에 다른 데이터는 필요 없고 장비를 구분할 수 있는 장비의 ID 값(deivceId)과 배터리값(battery)만 받고 싶다면 아래와 같이 쿼리문을 수정하면 됩니다.

Select deviceId, battery From `factory/room1/temperature` Where battery < 10

주의 토픽을 감싸는 특수문자는 `(Grave accent)를 사용하셔야 합니다. '(Single Quote)와 혼동에 주의하셔야 합니다.

액션 생성

액션은 트리거 조건에 부합되는 경우 실행되는 동작을 의미하며 현재 Cloud IoT Core에서는 "지정한 토픽으로 재발행", "Cloud Functions로 데이터 전송"의 2가지 기능을 지원합니다. 차후 다양한 액션 설정이 가능하도록 기능이 점차 확대될 예정입니다. 액션을 생성하려면 트리거 설정의 "쿼리검증" 이후에 생성이 가능하고, 1개의 트리거에는 반드시 1개 이상의 액션을 설정해야 합니다. 액션은 최대 5개까지 설정할 수 있습니다.

img

지정한 토픽으로 재발행

"지정한 토픽으로 재발행" 액션은 IoT 장비에서 발행되는 MQTT 메시지를 다른 이름의 토픽으로 재발행할 때 사용합니다. 예를 들면, IoT 장비가 발행하는 메시지 중 사용자가 사전에 정의한 규칙(트리거) 조건에 매칭된 데이터가 감지된 경우 alert라는 토픽으로 메시지를 재발행할 수 있습니다. (alert 재발행 메시지를 수신한 장비에서는 경고음을 울리게 하는 등 후속 작업이 가능합니다.)

img

  1. 액션의 드롭다운 메뉴에서 "지정한 토픽으로 재발행"을 선택 후 추가 버튼을 누릅니다.

  2. 액션 생성 팝업창의 재발행 토픽 항목에 메시지를 발행할 새로운 토픽 이름(ex: alert)을 입력합니다.

    (토픽 이름은 메시지가 무한대로 재발행 되는 것을 막기 위해 수신된 메시지와 동일한 토픽 이름으로 설정할 수 없습니다.)

  3. 추가 버튼을 눌러 생성을 완료합니다.

Cloud Functions로 데이터 전송

Cloud Functions로 데이터 전송을 하여 추가적인 액션을 수행하려면 먼저 Cloud Functions 상품에 가입해야 합니다. (가입되어 있지 않은 경우 이용 신청을 위한 팝업창이 생성되며 "Cloud Functions 신청" 버튼을 통해 Cloud Functions 상품으로 이동하여 이용 신청을 합니다.) Cloud Functions로 데이터 전송을 액션으로 선택하기에 앞서 Cloud Functions의 액션 및 트리거 설정 단계를 진행합니다. IoT 장비에서 Cloud IoT Core로 전송한 메시지를 분석하여 앞서 정의한 룰(트리거)에 일치하는 경우 Cloud Functions에 정의한 액션(예 : ITFFF를 통한 기기 제어) 또는 Cloud Functions의 새로운 트리거를 실행하는 구조입니다.

img

  1. 액션의 드롭다운 메뉴에서 "Cloud Functions로 데이터 전송"을 선택 후 추가 버튼을 누릅니다.
  2. 액션 생성 팝업창이 생성되며 사용자가 Cloud Functions에서 생성한 Cloud Functions의 액션 및 Cloud Functions의 트리거를 조회하고 선택할 수 있습니다.
  3. 완료 버튼을 눌러 액션 생성을 완료합니다.

에러 액션 생성

에러 액션은 사용자가 정의한 액션이 동작 중 실패하는 경우 실행되는 액션입니다. 예를 들어 "Cloud Functions로 데이터 전송" 액션을 요청하는 과정에서 예기지 못한 상황으로 Cloud Functions로의 요청이 실패했다면 Cloud IoT Core는 액션 실패를 인지하고 "에러 액션"으로 설정된 액션을 수행하게 됩니다.(Cloud Functions의 실행 결과에 대한 에러 처리는 지원하지 않습니다.) 에러 액션도 액션 설정과 동일하게 "지정한 토픽으로 재발행" 또는 "Cloud Functions로 데이터 전송"으로 설정할 수 있습니다.

참고 생성된 규칙(Rules)을 활성, 비활성, 삭제하는 방법은 부가 기능 페이지에서 확인하실 수 있습니다.

IoT 장비 연결하기

IoT 장비에 인증서를 설치하고 Cloud IoT Core 서버에 규칙 설정이 완료되면 두 장비 사이에 연결을 생성하고 메시지를 발행, 구독하는 과정이 정상적으로 동작하는지 확인할 수 있습니다.

IoT 장비에서 메시지를 발송하기 위한 예제코드(Java, Python, Java Script)는 다음의 링크를 참조하시기 바랍니다. 예제코드 링크

본 가이드에서는 Python 예제 코드를 기준으로 설명합니다.

  1. Cloud IoT Core 서비스 신청 후 다운로드한 인증서(xxxx.caChain.pem, xxxx.cert.pem, xxxx.private.pem, rootCaCert.pem)를 IoT 장비로 복사합니다.

  2. paho-MQTT 라이브러리를 파이썬 버전에 맞추어 설치합니다.

    pip install paho-mqtt //python 2.x
    python -m pip install paho-mqtt // python 2.7.9+
    python3 -m pip install paho-mqtt //python 3.x
    
  3. 데이터를 보내기 위한 코드 (mqttTSLClient.py) 작성 및 실행

    • python 예제코드의 mqttTLSClient.py 파일을 참고하여 코드를 작성합니다.
    • 예제 코드 내 인증서 경로와 사용자가 다운로드한 인증서 경로가 일치하도록 수정해야 합니다.
    • 예제 코드는 Cloud IoT Core에 접속 후 총 5번의 메시지 발행 및 구독을 반복한 후 프로그램이 종료합니다.
    • 메시지 발행은 콘솔 설명서의 예제와 같은 factory/room1/temperature 토픽으로 발생하며, IoT 서비스를 거쳐 재발행된 alert 토픽을 구독합니다.

Python 예제 코드

```
# coding=utf-8
from config import *
import argparse
import logging
import paho.mqtt.client as mqttClient
import ssl
import time
import json

def main():
    # init
    initLogger()
    initParameter()
    appendCert()

    # Connect MQTT Broker
    client = mqttClient.Client()
    if not connectTls(client, hostname, port, rootCa, fullCertChain, private):
        client.loop_stop()
        exit(1)

    attempts = 0
    while attempts < 5:
        # Subscribe message
        client.subscribe("alert",0)
        # Publish Message to Message broker
        publish(client, topic, payload)
        time.sleep(publishDelay)
        attempts += 1

    time.sleep(5)

def initLogger():
    global log
    log = logging.getLogger(__name__)
    log.setLevel(logging.ERROR)

    # stream handler (print to console)
    streamHandler = logging.StreamHandler()

    # file formatter
    formatter = logging.Formatter('[%(asctime)s] [%(levelname)s] (%(filename)s:%(funcName)s:%(lineno)d) : %(message)s')
    streamHandler.setFormatter(formatter)

    log.addHandler(streamHandler)

def initParameter():
    global rootCa
    global caChain
    global cert
    global private
    global fullCertChain

    global hostname
    global port
    global publishDelay

    global topic
    global payload

    global connected

    rootCa = '/<Your>/<file>/<path>/rootCert.pem'
    caChain = '/<Your>/<file>/<path>/caChain.pem'
    cert = '/<Your>/<file>/<path>/cert.pem'
    private = '/<Your>/<file>/<path>/private.pem'
    fullCertChain = '/<Your>/<file>/<path>/fullCertChain.pem'

    hostname = 'msg01.cloudiot.ntruss.com'
    port = 8883
    publishDelay = 1 # sec

    topic = "factory/room1/temperature"
    payload = "{ \"deviceId\": \"device_1\", \"deviceType\": \"temperature\", \"value\": 35, \"battery\": 9, \"date\": \"2016-12-15\", \"time\": \"15:12:00\"}"

    connected = False

def appendCert():
    filenames = [cert, caChain]
    with open(fullCertChain,'w') as outfile:
         for fname in filenames :
            with open(fname) as infile:
                outfile.write(infile.read()+"\n")

def connectTls(client, hostname, port, rootCa, fullCertChain, clientKey):
    client.on_connect = on_connect
    client.on_message = on_message
    client.on_publish = on_publish

    client.tls_set(ca_certs=rootCa, certfile=fullCertChain,
                   keyfile=clientKey, cert_reqs=ssl.CERT_REQUIRED,
                  tls_version=ssl.PROTOCOL_TLSv1_2, ciphers=None)

    ssl.match_hostname = lambda cert, hostname: True
    client.tls_insecure_set(False)
    client.connect(hostname, port=port)
    client.loop_start()

    attempts = 0

    while not connected and attempts < 5:  # Wait for connection
        time.sleep(1)
        attempts += 1

    if not connected:
        return False

    return True

def on_connect(client, userdata, flags, rc):
    if rc == 0:
        log.info("=== Successfully Connected")
        global connected  # Use global variable
        connected = True  # Signal connection
    else:
        log.error("=== Connection lost")

def on_publish(client, userdata, result):
     print(">>> Publish to IoT server.")

def publish(client, topic, payload):
    try:
        client.publish(topic, payload)
    except Exception as e:
        print("[ERROR] Could not publish data, error: {}".format(e))

def on_message(client, userdata, message):
    print("<<< Subscribe from IoT server. topic : "+ message.topic + ", message : " + str(message.payload.decode("utf-8")))


if __name__ == "__main__":
    main()

```

**Python 예제 코드 동작 결과**
```
>>> Publish to IoT server.
>>> Publish to IoT server.
>>> Publish to IoT server.
>>> Publish to IoT server.
<<< Subscribe from IoT server. topic : alert, message : {"battery":9,"date":"2016-12-15","deviceId":"device_1","deviceType":"temperature","time":"15:12:00","value":35}
<<< Subscribe from IoT server. topic : alert, message : {"battery":9,"date":"2016-12-15","deviceId":"device_1","deviceType":"temperature","time":"15:12:00","value":35}
<<< Subscribe from IoT server. topic : alert, message : {"battery":9,"date":"2016-12-15","deviceId":"device_1","deviceType":"temperature","time":"15:12:00","value":35}
>>> Publish to IoT server.
<<< Subscribe from IoT server. topic : alert, message : {"battery":9,"date":"2016-12-15","deviceId":"device_1","deviceType":"temperature","time":"15:12:00","value":35}
<<< Subscribe from IoT server. topic : alert, message : {"battery":9,"date":"2016-12-15","deviceId":"device_1","deviceType":"temperature","time":"15:12:00","value":35}
```

""에 대한 건이 검색되었습니다.

    ""에 대한 검색 결과가 없습니다.

    처리중...