소마(Software Maestro, http://swmaestro.kr/) 6기 1단계 JJK팀의 발표 자료입니다.
(2015.7월~8월 수행)
IoT 데이타 수집 및 실시간 모니터링을 주제로 하여 센서, 게이트웨이, 서버, 웹 까지 개발한 것입니다.
1) Arduino, Raspberry Pi 등에 CO2 공기측정, 온도 등의 환경센서와
체온, 혈압, 심박수, 산소포화도 등의 바이오 센서를 연결하고 센서 데이타를 모아서 서버로 전송하고,
2) 서버에서는 MQTT, Redis 를 이용하여 데이타를 수집하고 저장합니다.
3) 웹에서는 jQuery, Highcharts 를 이용하여 센서 데이타를 그래표 차트로 시각화하고,
socket.io를 이용하여 실제 센서에서 발생한 데이타를 실시간으로 웹 화면에 표현합니다.
한대희 멘토 - https://www.facebook.com/profile.php?id=100000899505079
6. Arduino & Raspberri Pi
•Arduino : Digital I/O Pin과 Analog I/O Pin이 존재하므로 센서 값을
측정하는 데 사용
•Raspberri Pi : Raspbian OS, USB, 이더넷 등이 지원되므로 node.js,
Python, MQTT 등을 사용
•Arduino와 Raspberri Pi의 UART(serial) 통신을 위해 USB케이블 연결
7. MQTT
(Message Queue Telemetry Transport)
•단일 TCP만을 사용하여 최소의 자원으로 작동
•임베디드처럼 한정된 자원을 가진 시스템에 적합
•Publish/Subscribe 모델
•특정 메시지들을 송수신하기 위하여 Topic을 통해 분류
•Publisher와 Subscriber는 서로 알 수 없으며, Broker를 통하여 메시지를 송수신
8. Code of Gateway
serialPort.on('open', function () {
// serialport를 통해 data가 들어오면 핸들러를 실행한다.
// data: { topic, value }
serialPort.on('data', function (data) {
var data = JSON.parse(data);
for(var topic in data) {
// epochtime과 value의 배열을 생성한다.
var message = [ getEpochTime(), data[topic] ];
message = JSON.stringify(message);
mqtt_publisher.publish(topic, message);
});
});
9. Code of Gateway
mqtt_publisher.subscribe(‘cmndFromSvr’);
// subscribe하고 있던 topic에 대한 message가 들어오면 핸들러를 실행한다.
mqtt_publisher.on('message', function (topic, message) {
var message = JSON.parse(message);
// 서버로부터 받은 명령에 따라 각 actuator를 실행시킨다.
switch(message.actuator) {
case 'LED' :
if(message.cmd.toString() == 'TURNON') {
serialPort.write('0');
}else {
serialPort.write('1');
}
break;
case 'PIEZO' :
if(message.cmd.toString() == ‘SOUND') {
serialPort.write('3');
}
break;
}
});
11. Redis
•Redis : NoSQL 기반의 데이터베이스
•특징
• In-Memory 기반의 데이터베이스
- 속도가 매우 빠름
- 데이터 저장방식: RDB, AOF
• Key-Value 형태의 데이터베이스
• 다양한 데이터 타입 지원
- List, Sorted Set, Hashes …
12. Code of Redis
// collector.js
// mqtt client에 메시지가 들어오면 redis에 lpush한다.
mqtt_client.on(‘message’, function (topic, message) {
redis_client.lpush(topic.toString(), message);
});
// app.js
// 웹 브라우저로부터 요청이 들어오면 redis db의 값을
보내준다.
app.get(‘/api/current’, function (req, res) {
sensorname = req.query.sensorname;
db.lindex(sensorname, 0, function (err, data) {
res.json(data);
});
});
14. Express & EJS
•Express : 웹과 모바일 애플리케이션을 위한 다양한 기능을 제공하는
작고 유연한 node.js 웹 프레임워크
•EJS : Template Rendering Engine
•Code of Express
var express = require('express');
var app = express();
var ejs = require(‘ejs’);
var http = require(‘http’);
var server = http.createServer(app);
app.set('port', process.env.PORT || 8080);
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs'); // set the view engine to ejs
app.get('/current', function (request, response) {
response.render('pages/current', { });
});
15. Socket.io
• Web Socket과 달리 Socket.io는 node.js에서 바로 사용할 수 있는 기술
• JavaScript를 이용하여 브라우저 종류에 상관없이 실시간 웹을 구현할 수 있도록 한 기술
• 브라우저와 웹 서버의 종류와 버전을 파악하여 가장 적합한 기술을 선택하여 사용
16. Code of Collector
var request = require('request');
var formdata = {
'to' : toAddr,
'from' : fromAddr,
'subject' : subject,
'html' : message
};
var options = {
url: ‘/noti',
form: formdata
};
// request 모듈을 이용하여 서버에 post방식으로 보낸다.
request.post(options, function (err, httpResponse, body) { … });
17. Code of Web
Server (app.js)
// connection이 발생할 때 핸들러를 실행한다.
ioserver.sockets.on('connection', function (socket) {
// 클라이언트에서 joinroom 이벤트가 발생하면 데이터를 받는다.
socket.on('joinroom', function (roomname) {
socket.join(roomname);
});
});
// event notification을 통해 post 요청을 처리한다.
app.post(‘/noti’, function (req, res) {
// 클라이언트로 redraw 이벤트를 보낸다.
ioserver.sockets.in('CURRENT').emit('redraw', req.body);
});
Client (current.ejs)
// 서버에 joinroom 이벤트를 보낸다.
socket.emit(‘joinroom', ‘CURRENT’);
// 서버에서 redraw 이벤트가 발생하면 데이터를 받는다.
socket.on('redraw', function (data) {
var sensorname = data.sensorname;
var point = data.point;
updateChart(sensorname, point);
});
18. Ajax with jQuery
•페이지 일부분을 업데이트 하기 위한 정보를 서버에 요청할 수 있다.
•서버로부터 받은 데이터로 작업을 한다.
•Code of Web Browser
$.ajax({
url: '/api/current',
data: { sensorname: sensorid },
success: function(point) {
$('#' + sensorid).html(point);
},
cache: false
});