MQTT에 관심을 가지는 이유는 (저전력/소규모)디바이스와 IoT 인프라간의 통신 프로토콜로의 가능성 때문이다. IoT 인프라는 소규모 디바이스 뿐만 아니라 PC, 타블렛, 모바일 기기와 같은 비교적 강력한 디바이스가 붙을 수 있다. 온갖 종류의 디바이스들과 애플리케이션들이 혼재한 공간이다. 현재 이 공간을 묶어주기 위해서 가장 일반적으로 사용하는 도구들은 HTTP, HTML, Javascript이다. 인터넷은 IoT의 한축이다. IoT 인프라 개발자로서 [MQTT와 인터넷(웹)을 묶는걸 고민할 수 밖에 없다.
내가 생각하는 MQTT와 웹과의 연동방식은 다음과 같다.
HTTP : 입력과 출력을 분리 할 경우, 입력은 HTTP로 보낼 수 있다. 채팅 서비스를 예로 들자면, 메시지 입력은 HTTP 로 보
JavaScript : Javascript의 MQTT 라이브러리를 이용해서 직접 MQTT기반 통신을 할 수 있다.
모바일 기기의 타입에 따라서 다양한 조합이 가능 할 것이다. HTTP + WebSocket, HTTP + Javascript, HTTP + MQTT Client(아마 하이브리드 앱이 될 것이다.), 입출력 모두에 MQTT를 이용하는 전용 앱등의 다양한 조합이 가능하다.
내가 관심을 가지고 있는 조합은 HTTP+WebSocket, HTTP+Javascript 조합으로, 이 두가지 모두를 테스트할 계획이다.
HTTP + WebSocket 조합
테스트 환경
운영체제 : 우분투 리눅스 14.04
Ruby 2.1.2 : 우분투 리눅스 14.04에 들어있는 루비는 버전 1.9.x다. 최근 레일즈 공부하면서 2.1.2로 업데이트 했다. 1.9로 테스트해도 문제 없을 거라 생각하는데, 이번 기회에 그냥 업데이트 하자.
VirtualBox로 Sensor Device를 만들었다. 온도와 습도를 측정하는 디비이스다(물론 가상의 디바이스다. MQTT Client로 주기적으로 온도와 습도 정보를 MQTT 브로커에 Pub 하는 식으로 작동한다). MQTT 브로커로 mosquitto를 사용했다.
MQTT Push Server는 인터넷으로 확장하면 IoT 인프라의 구성요소일 수 있겠고, 로컬 네트워크에 두면 "IoT 홈 게이트웨이"가 될 수 있을 거다. 여기에서는 IoT 홈게이트웨이로 가정한다.
테스트 시나리오는 다음과 같다.
Web Browser로 Push Server에 접근한다.
웹 서버는 유저 인터페이스를 출력하고, 유저 연결을 Websocket 으로 변환한다.
Websocket은 Ruby-MQTT 라이브러리를 이용해서 MQTT-Broker에 sensor topic을 구독신청(subscribe)한다.
구독정보는 Websocket를 통해서 웹 브라우저로 전송하고, 웹 브라우저는 javascript를 이용해서 화면에 표시한다.
Thin + Sinatra 설치
[wkki:Site/Ruby/sinatra/UbuntuInstall Thin, Sinatra 설치 참고] : Monk를 이용해서 웹 애플리케이션 구조를 잡았다.
websocket 설치
sinatra-websocket을 설치했다.
# gem install sinatra-websocket
Ruby mqtt gem 설치
# gem install mqtt
EventMachine 기반의 em-mqtt도 있다. Thin 웹서버도 em 기반이기 때문에 em-mqtt와 궁합이 잘 맞을 것이다. 하지만 내가 사용하는 ruby 2.1.2에서는 em-mqtt가 제대로 실행되지 않았다. 검색해본 결과 Ruby 1.9.x 이상 버전에서는 잘 작동하지 않는 문제가 보고됐다고 한다. 해서 그냥 mqtt를 사용했다.
Sinatra Code
아래는 시나트라 코드다. 설명은 주석으로 대신한다.
# encoding: utf-8
require 'sinatra-websocket'
require 'mqtt'
require 'pp'
require 'time'
class MyApp < Sinatra::Application
# 유저 화면을 출력한다.
# index 페이지는 MQTT 서버 정보입력을 위한 UI와
# MQTT 서버로 부터의 Push 메시지를 출력하기 위한 UI가 있다.
get "/" do
@title = "MQTT Push Test"
erb :index
end
# 유저가 Websocket을 사용하지 않고 /mqtt에 접근할 경우
# 에러페이지를 출력한다.
get "/error" do
erb :error
end
# index 페이지에서 MQTT 서버정보를 입력하고 submit 하면
# websocket 페이지로 프로토콜 change 한다.
# qos는 사용하지 않는다.(귀찮아서)
get "/mqtt" do
ip = params[:ip]
port = params[:port]
qos = params[:qos]
topic = params[:topic]
# websocket 연결이 아니면 에러 페이지로 보낸다.
if !request.websocket?
erb :error
else
request.websocket do |ws|
ws.onopen do
# MQTT 서버에 연결한 후 topic을 subscribe 한다.
MQTT::Client.connect(:host=>ip, :port=>port) do | conn |
conn.get(topic) do |topic, message|
message = message.force_encoding("UTF-8")
ws.send "{\"time\":\"#{Time.new.to_s}\",\"msg\":\"#{message}\"}"
end
end
end
ws.onmessage do |msg|
end
ws.onclose do
puts "Web socket close"
end
end
end
end
end
Index 페이지
Index 페이지는 "MQTT 서버 정보 입력을 위한 UI"와 Push 메시지를 받기 위한 websocket, 메시지를 출력하기 위한 인터페이스로 구성된다. 화면은 다음과 같다.
https://lh6.googleusercontent.com/-lLs1QGDyBHE/VEt-2OKeBmI/AAAAAAAAEcw/DEc7FUa1KzI/s800/mqtt_01.png
유저 인터페이스는 foundation css 프레임워크
와 과 jquery 조합으로 만들었다.
반드시 --threaded 옵션을 이용해서 스레드 모드로 실행 해야 한다. 앞서 언급했듯이, thin은 기본적으로 EM 모드로 작동한다. 유저 요청을 비동기로 처리할 거라면, 서버의 다른 모든 입출력로 비동기 모드로 작동해야한다. 예제의 경우 mqtt.get 호출 부분에서 블럭돼 버린다. 하나의 유저만 처리할 수 있다는 이야기. 비동기 방식의 em-mqtt를 사용하면 되겠지만, 현재 버전의 루비에서는 제대로 작동을 하지 않아서, 굳이 스레드 모드로 실행 했다.
Contents
웹과 조합하려는 이유
HTTP + WebSocket 조합
테스트 환경
구성 및 시나리오
Thin + Sinatra 설치
websocket 설치
Ruby mqtt gem 설치
Sinatra Code
Index 페이지
테스트
테스트 코드 다운로드
Push 서버 실행
Pub 테스트
기타 하고 싶은 것들
Javascript MQTT
비동기 구현
IoT 환경에서의 MQTT
Recent Posts
Archive Posts
Tags