경상북도 구미시 봉곡북로 65

명품빌딩 3층, (주)네오텍

Send Your Mail At:

semineotech@gmail.com

블로그

네오텍 블로그 입니다.

벨 소리를 듣고 문 열어 주기

도어락

사무실 이전을 하면서 보안 업체와 계약을 하지 않고 도어락을 설치했습니다.

도어락을 사용한 후 제일 불편한 점은 손님이 방문했을 때 누군가가 자리에서 일어나 사무실을 나가 로비를 지나 현관문까지 가서 도어락의 버턴을 눌러야 하는 것입니다.

물론 우리 사무실이 너무나 넓어서 도어락을 열기 위해 엄청난 거리를 걸어 가야 하는 것은 아닙니다만 모두가 바쁠 때는 아무도 문을 열어주지 않는 경우도 있습니다.

그래서 도어락 열림 장치를 고민하게 되었습니다.

열림 버턴 누르기

도어락의 열림 버턴을 누르기 위해 사람 손을 대신할 액추에이터를 설치하는 방법이 있습니다.

하지만, 액추에이터를 사용하기에는 여러가지 문제가 있습니다.

기계장치를 도어락에 부착하는 문제, 모터 구동을 위한 전원 공급, 적당한 액추에이터 구하기, 가격 문제등 차라리 온라인 제어가 되는 최신식 도어락을 구입해 설치하는 편이 더 나아 보였습니다.

기계장치 대신 버턴을 눌렀을 때와 동일한 신호를 발생할 수 있는 전자 장치를 추가하는 방법을 생각해 봤습니다.

보통 키 입력 회로는 키를 눌렀을 때 MCU의 GPIO 입력 전압을 GND로 변경합니다.

도어락을 분해해 키 입력 부분에 트랜지스트를 추가한 후 레벨을 변경해 보았습니다.

예상대로 키 눌린 것과 동일하게 동작합니다.

벨 소리 인식 방법

누군가가 현관문에 설치된 벨을 눌렀을 때 도어락을 열어줘야 합니다.

벨을 누른 것은 소리로 인식합니다. 사무실 안쪽에 스피커가 설치되어 있으니 전기 신호를 인식할 수도 있겠지만, 천장으로 배선된 케이블을 찾아 작업하기가 쉬워 보이지 않았습니다.

소리 인식은 TensorFlow/docs 에 있는 Simple Audio Recognition 을 이용합니다. Source code는 여기에 있습니다.

샘플 코드 동작

다운로드한 샘플 코드를 Linux 시스템에서 테스트 해 봅니다. 아래 명령을 입력해 학습을 시도합니다.

$ python3 train.py

ImportError가 발생해 Tensorflow를 업그레이드 했습니다.

또한, root 디렉토리에 /tmp 를 생성하도록 설정되어 있어 퍼미션 에러 방지를 위해 ./tmp로 수정합니다.

에러를 수정하고 실행하면 dataset을 다운로드하여 학습을 진행합니다.

벨 소리 학습

학습을 위한 데이터 확보를 위해 회사 동료들에게 벨 소리 녹음을 부탁했습니다.

녹음된 파일을 받아 벨 소리 부분을 4.5초 길이로 잘라서 16KHz로 샘플링된 50개의 데이터 파일을 만듭니다.

샘플 코드에서 사용하는 데이터셋의 구조에 맞게 dingdong 디렉토리를 생성하여 학습용 데이터 파일을 복사합니다. _background_noise_와 others는 샘플 데이터셋을 그대로 사용합니다.

수정된 환경에 맞게 옵션을 조정하여 학습을 진행합니다. 데이터의 차이가 거의 없기 때문에 정확도 100%로 학습됩니다.

$ python3 train.py --data_url= --data_dir=./tmp/my_dataset/ --wanted_words=dingdong --clip_duration_ms=4500 --how_many_training_steps=1200,400 --batch_size=10

Android App에서 사용할 파일을 생성할 옵션도 환경에 맞게 조정합니다.

$ python3 freeze.py  --sample_rate=16000 --wanted_words=dingdong --clip_duration_ms=4500 --how_many_training_steps=1200,400 --batch_size=10 --start_checkpoint=./tmp/speech_commands_train/conv.ckpt-1600 --output_file=./tmp/dingdong_graph.pb

Android App 동작

안드로이드 앱에서 학습한 결과를 이용하여 벨 소리를 인식하기 위해 Tensorflow ExampleTF Speech를 이용합니다.

이 클래스가 오디오 스트림을 입력받아 판별하는 방식을 요약하면 아래와 같습니다.

  • recording thread에서 recordingBuffer에 입력된 오디오 데이터를 기록한다.
  • recordingBuffer는 원형 큐로 동작하며, bufferOffset이 저장할 위치를 지정한다.
  • recordingBuffer의 크기는 training에 사용한 dataset의 길이와 샘플링 주파수로 결정된다.
    • recordingBuffer의 크기 = dataset의 길이(초) * 샘플링 주파수
  • 녹음된 데이터 버퍼는 recordingBuffer에 비해 작다.
  • 인식하는 thread에서는 recordingBuffer의 bufferOffset 부터 시작해 전체 데이터를 읽어 다른 버퍼에 저장한다.
  • 따라서 전체 데이터의 길이는 dataset의 길이와 같다.
  • 이 데이터를 TF로 보내 판별한다.

앱에서 벨 소리 학습 데이터를 사용해 벨 소리를 인식하기 위해서 몇 가지 인수를 조정해야 합니다. 아래는 조정한 인수에 대한 설명입니다.

이름기본값변경값설명
minimumTime
Between
SamplesMs
30ms짧은 시간동안 테스트를 많이 하는 것을 방지하기 위해, 테스트한 후 이 시간동안 테스트를 하지 않고 기다립니다.
averageWindow
DurationMs
500ms1500ms이 시간동안 테스트 한 결과를 평균해 최종 판단을 합니다.
minimumCount3최종 결과를 얻기위해 averageWindowDurationMs 동안 minimumCount 이상 테스트를 해야합니다. 너무 짧은 시간동안 테스트를 마치는 경우를 방지하기 위해 총 테스트 시간은 (averageWindowDurationMs / 4) 이상이어야 합니다. 이것은 minimumTimeBetweenSamplesMs를 이용해 조정 할 수 있습니다.
detectionThreshold 0.7TF에서 테스트한 점수의 평균이 이 값 이상이어야 인식된 것으로 판단합니다.
suppressionMs 1500ms인식된 결과를 전달한 후 다음 결과를 전달하기 까지의 대기 시간입니다.
동일한 결과가 연속해서 전달되는 것을 방지합니다.

시스템 구성

동작 시스템은 단순합니다. 벨 소리가 나면 안드로이드 앱에서 인식을 하고 블루투스 모듈로 문을 열라는 신호를 보낸니다. 블루투스 모듈은 도어락의 버턴을 전기 신호로 제어하여 문을 엽니다.

그런데, 이 구성도는 소비 전력 문제가 있습니다. 블루투스 모듈은 수신 대기를 해야 하므로 항상 액티브 상태에 있어야 합니다. 이 경우 블루투스 모듈의 소비전류는 20mA 정도입니다. 1000mAh 건전지를 사용한다면 이틀마다 교체해야 합니다.

이 글을 쓰면서 같은 칲셋을 사용하는 모듈을 찾아 보았습니다. 20mA 정도면 열심히 데이터를 주고 받을 때의 소비전류입니다. S/W가 뭔가 다른가 봅니다.

블루투스 모듈의 소비 전력 문제는 좀 더 검토를 해 봐야겠습니다. 그런 후에 도어락에 부착할 수 있을 것 같습니다.

GPS Timeline

개요

GPS 센서를 이용하여 GPS 디바이스를 제작하고 Lora 를 이용해 이동경로를 서버에 저장 또는 지도에 표시 하는 시스템을 제작해 봅니다. 구글 클라우드로 서버를 구축하는 것은 덤.

시스템 구성

시스템 구성은 크게 3가지로 나뉩니다.

  • GPS 보드 : GPS 센서, MCU 보드, Lora 모듈
  • 구글 클라우드 가상 서버 : 구글 클라우드, Node.js 서버
  • 네오텍 서버 : MongoDB

어떻게 GPS 데이터를 저장하게 되나요.

먼저 MCU 보드가 GPS 센서로 부터 GPS 데이터를 받아 LoRa 모듈에게 전달합니다. Lora 모듈은 GPS 데이터를 SKT 서버인 ThingPlug 로 전달 하죠. 그러면 구글 클라우드에서 구축한 Node.js 서버가 GPS 데이터를 ThingPlug로부터 전달 받고 마지막으로 네오텍 서버에 GPS 데이터를 저장시킵니다.


LoRa에 대해서 알아볼까요?

1. LoRa란?

Long Range의 약자로 LoRa Alliance 사에서 만든 비면허 통신 기술입니다. IoT 통신을 위해 만들어진 통신규격으로 초장거리연결과 저전력으로 좋은 환경에서 최대 10마일(16km) 정도의 통신이 가능하며 별도의 유심이 필요없이 센서에 할당된 노드번호를 기반으로 통신합니다.

국내에서는 SK텔레콤만 서비스하며 대역은 915Mhz대역을 통해 서비스합니다.

2. LoRa 데이터 수집 방법

LoRa 디바이스의 데이터는 LoRa GW와 N/W서버를 거쳐 ThingPlug에 기록되며 ThingPlug 에서 데이터를 가져 오는 방식은 기본적으로 Polling 방식과 Subscription 방식 2가지가 있으며 둘 중 Subscription 방식을 택하여 데이터를 가져오도록 하겠습니다. Subscription 방식은 LoRa 디바이스에서 ThingPlug 로 값 전달 시 ThingPlug에 저장된 값이 달라질 경우 데이터를 가져오는 방식입니다.

3. LoRa모듈 선택

LoRa는 국내에서 SKT만 서비스한다고 알려드렸습니다. 그러면 LoRa를 사용하기 위해 SKT에서 지원하는 LoRa 모듈을 사용하려고 하는데 크게 성지산업과 솔루엠의 Lora모듈이 있습니다. 두 회사의 모듈은 차이점이 4가지가 있는데 간단히 알아봅시다.

  • Baud rate가 다르다. 성지산업:115200, 솔루엠:38400
  • 메시지 형식이 다르다. 성지산업:ASCII, 솔루엠:Hex
  • Command 형식이 다르다. 성지산업: CLI, 솔루엠:AT
  • Wake-up 처리 방식이 다르다. 성지산업: sleep 모드에서 UART 사용X, 솔루엠: 사용O

둘 중 성지산업(방식)의 LoRa 모듈인 F1PLM-01-A(외장형 LoRa 모뎀)을 선택하여 GPS 보드를 제작하도록 하겠습니다.

참고 사이트: SKT IoT Portal


GPS 보드

1.구성

  • GY-GPSV3-NEO(GPS 센서)
  • F1PLM-01-A(외장형 LoRa 모뎀)
  • WEMOS 보드(MCU 보드)

2. 연결도

3. 연결 조건 및 기타 사항

  • GPS 센서
  • LoRa 모듈
    • Baud rate는 115200bps 이다.
    • 1분 간격으로 GPS 데이터를 전달한다.
    • UART 통신 프로토콜로 CLI Command를 사용한다.
    • 구조는 ‘LRW [Command ID] [Option] CR LF’로 구성된다.
    • LoRa 모듈은 CLI Command를 받을 준비가 되면 “READ” 를 전달한다.
    • LoRa 모듈은 CLI Command를 받아 처리 후 “READ”를 전달 하기 까지 대략 5~6초의 시간이 소요된다.

Node.js

Node.js로 웹 서버를 제작하여 인터넷으로 GPS 이동경로를 표시한 지도를 확인해 봅시다.

1. 개발 환경

  • Node.js를 설치합니다.
  • Visual Studio 2017-2019를 설치합니다. Visual Studio 2017 이상에서는 Node.js 서버의 개발을 지원합니다.
  • 기본 Node.js Express 4 애플리케이션(javaScript)으로 프로젝트를 생성합니다.
  • 웹 서버가 동작하는지 실행시켜 봅니다.

2. 기능 구현

  • MQTT 통신 코드 작성
  • MongoDB에 GPS 데이터 저장
  • 메인페이지에서 날짜를 선택하여 해당 날짜의 GPS 데이터를 지도에 표시
    • 메인페이지에서 달력으로 날짜 선택
    • MongoDB GPS 데이터 읽어오기
    • Google 지도에 GPS 데이터 마커로 표시하여 보여주기

3. MQTT 통신 코드 작성

  • npm(node package manager) 을 이용하여 mqtt 패키지를 설치합니다.
  • mqtt 초기화
var mqtt = require('mqtt');
var PORT = 8883;   // mqtts 포트
var HOST = 'thingplugpf.sktiot.com';

var options = {
    host: HOST,
    port: PORT,
    protocol: 'mqtts',
    protocolId: 'MQIsdp',
    secureProtocol: 'TLSv1_2_method',   // TLSv1.2
    protocolVersion: 3,
    rejectUnauthorized: false,
    username: '****',   // ThingPlug 계정
    password: '****',   // ThingPlug 계정 사용자 인증키
    clientId: '****'   // ThingPlug clientId
};
  • mqtt 연결
var client = mqtt.connect(options);

client.on('connect', function () {
    console.log('MQTT Connected:' + client.connected);
    client.subscribe("/oneM2M/resp/[clientId]/+", { qos: 0 });  // mqtt subscribe
});
  • mqtt 메시지 확인
client.on('message', function (topic, message) {
    console.log(message.toString());
});

4. MongoDB에 GPS 데이터 저장

  • npm(node package manager) 을 이용하여 express-session, connect-mongo, mongoose 패키지를 설치합니다.
  • mongoDB 초기화 > app.js
var session = require('express-session');
var MongoStore = require('connect-mongo')(session);

var conf = {
    db: {
        url: '****',  // mongoDB url
        collection: 'sessions'
    },
    secret: '****'
};

app.use(session({
    secret: conf.secret,
    resave: false,
    saveUninitialized: true,
    maxAge: new Date(Date.now() + 3600000),
    store: new MongoStore(conf.db)
}));
  • mongoDB 연결
var mongoose = require('mongoose');
var db = mongoose.connection;

db.once('open', function () {
    dbg.log('DB connection opened!');
});

db.on('connected', function () {
    dbg.log('DB Connected!');
});

mongoose.connect('[mongoDB url]', {
    useNewUrlParser: true,
    auth: {
        user: '****',   // mongoDB user
        password: '****'   // mongoDB password
    },
    auto_reconnect: true
 });
  • mongoDB 연결이 잘되는지 웹서버 실행
  • mongoDB에 GPS 데이터 삽입 및 얻기
var gpsSchema = mongoose.Schema({
    date: String,
    time: String,
    speed: String,
    latitude: String,
    longitude: String
});

var GpsData = mongoose.model('gpsSchema', gpsSchema);

exports.insertGpsData = function (date, time, speed, latitude, longitude, callback) {
    let gpsData = new GpsData();
    gpsData.date = date;
    gpsData.time = time;
    gpsData.speed = speed;
    gpsData.latitude = latitude;
    gpsData.longitude = longitude;
    gpsData.save(function (err, gpsData) {
        if (typeof callback === "function") {
            callback(err, gpsData);
        }
    });
}

exports.getGpsData = function (date) {
    return new Promise(function (resolved, rejected) {
        GpsData.find({ date: date }, function (err, results) {
            if (err === null) {
                resolved(results);
            } else {
                resolved(0);
            }
        });
    });  
}

5. 메인 페이지에서 달력으로 날짜 선택

이제 메인 웹 페이지를 만들어 봅시다. 웹 언어인 HTML 기반으로 작성하되Bootstrap 웹 프레임워크를 사용하여 간단하게 웹 페이지 를 제작해봅시다.

  • 헤더에 Bootstrap, jquery, DateTimePicker, 스타일을 포함 시킵니다.
  • 바디에 날짜 정보를 post로 전달 받을 수 있는 form 태그를 작성합니다.
  • javaScript로 달력의 UI와 동작을 추가합니다.
  • 작성된 main.html 파일을 실행해 봅시다.
  • Node.js Express4 프로젝트에서는 기본 view engine을 pug로 setting 하기 때문에 html 파일을 pug 형태로 변환하여 포함 시킵니다.
  • 변환사이트: pugHtml.com

6. Google 지도에 GPS 데이터 마커로 표시하여 보여주기

Google 지도에서 위치를 표시하여 보여주는 HTML 파일을 작성해 봅시다.

  • Google API console 에서 API key를 생성합니다.
  • 생성된 api key를 포함하여 google maps api와 markerclusterer 를 포함시킵니다. markerclusterer 는 gps 위치에 따라 찍힌 marker 가 모여있을 시 간략하게 표시해주는 역할을 합니다.
<!DOCTYPE html>
<head>
</body>
    ...
    <script src="https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/markerclusterer.js"></script>
    <script async defer
            src="https://maps.googleapis.com/maps/api/js?key=[API Key]&callback=initMap">
    </script>
</head>
  • Google Map 초기화, 대한민국 서울을 기준으로 합니다.
<script>
        var map;
        let markers = [];  
        var dronePath;
        var markerCluster;
        let markersIndex = 0;
        var SOUTH_KOREA_BOUNDS = {
            north: 38.81,
            south: 31.97,
            west: 123.75,
            east: 132.19,
        };
        var SEOUL = { lat: 37.56, lng: 126.97 };

        function initMap() {
            map = new google.maps.Map(document.getElementById('map'), {
                center: SEOUL,
                restriction: {
                    latLngBounds: SOUTH_KOREA_BOUNDS,
                    strictBounds: false,
                },
                zoom: 7,
            });
        ...
</script>
  • 원하는 위치에 마커를 찍고 지우는 함수
       function makeMarker(position, label) {
            return new google.maps.Marker({
                position: position,
                label: label,
                map: map
            });
        }
  
        function deleteMarkers() {
            for (var i = 0; i < markers.length; i++)
                markers[i].setMap(null);
            dronePath.setMap(null);
            markers = [];
            markerCluster.clearMarkers();
        }
  • 마커를 선택할 경우 메시지 표시
        function attachMessage(marker, message) {
            var infowindow = new google.maps.InfoWindow({
                content: message
            });

            marker.addListener('click', function() {
                infowindow.open(marker.get('map'), marker);
            });
        }
  • 마커를 따라 라인을 그립니다.
        function drawPloyline(arrDroneRoute, map) {
            dronePath = new google.maps.Polyline({
                path: arrDroneRoute,
                geodesic: true,
                strokeColor: '#FF0000',
                strokeOpacity: 1.0,
                strokeWeight: 2
            });

            dronePath.setMap(map);
        }
  • 다음과 같은 gps 데이터가 기록된 텍스트 문서를 읽고 파싱하여 지도에 그려봅시다.

[09:50:32,36.148524,128.330920]
[09:50:33,36.148524,128.330920]
[09:50:34,36.148528,128.330920]
[09:50:35,36.148524,128.330920]
[09:50:36,36.148520,128.330920]

        function openTextFile() {
            var input = document.createElement("input");
        
            input.type = "file";
            input.accept = "text/plain";
        
            input.onchange = function (event) {
                processFile(event.target.files[0]);
            };
        
            input.click();
        }
function processFile(file) {
    var reader = new FileReader();

    reader.onload = function () {
        //output.innerText = reader.result;
        var text = reader.result;
        let arrData = text.split(']');
        var position = [];
        for(var idx = 0; idx < arrData.length - 1; idx++)
        {
            var arrGpsData = [];
            arrGpsData = arrData[idx].trim().substring(1).split(',');
            position[idx] = new google.maps.LatLng(arrGpsData[1], arrGpsData[2]);
            markers[idx] = makeMarker(position[idx], null);

            attachMessage(markers[idx], arrGpsData[0]); // 시간 표시
        }
        
        drawPloyline(position, map);    // 라인 그리기
        map.setZoom(15);                // 지도 확대
        map.setCenter(position[0]);     // 시작 지점으로 맵 이동

        markerCluster = new MarkerClusterer(map, markers,
            {imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'});     // 마커 클러스터화
    };

    reader.readAsText(file, /* optional */ "euc-kr");
}
  • 결과물
  • 마찬가지로 작성한 html 파일을 pug 형태로 변환하여 프로젝트에 포함시킵니다.

7. 정리

  • GPS 데이터를 MQTT로 전달 받아 MongoDB 저장
  • main 페이지에서 날짜를 form > post로 전달
  • MongoDB 에서 해당 날짜의 GPS 데이터를 얻음
  • GPS 데이터를 파싱하여 지도에 그림

구글 클라우드 가상 서버

이제 제작한 Node.js 웹 서버를 운영할 서버가 필요합니다. 보기에 간단한 역할을 하는 서버인데 개인 컴퓨터로 웹서버를 운영하기에는 비용과 관리 측면에서 부담이 되는 것은 사실입니다. 그래서 이러한 부분들을 해결해 줄 수 있는 클라우드 서비스를 이용하여 웹 서버를 운영해 봅시다.

1. 가상 머신 생성

구글 클라우드의 Google Compute Engine을 사용하면 Google 인프라에서 가상 머신을 만들고 실행할 수 있습니다.

  • 새 VM 인스턴스를 생성합니다. Ubuntu 18.04 LTS 버전으로 설치합니다.

구글 클라우드에서는 f1-micro 머신을 생성하면 가상서버 1개 기준으로 한달(744시간) 무료입니다. 또한 디스크 용량은 30GB가 무료입니다.

참조 : https://cloud.google.com/compute/pricing?hl=ko

  • 가상머신 생성완료

2. Node.js 웹 서버 실행

가상서버에서 Node.js 웹 서버를 운영해봅시다.

  • 연결 > SSH 부분을 누르면 터미널 창이 띄워집니다.
  • Node.js, npm 을 설치합니다.
    • $ sudo apt-get update
    • $ sudo apt-get install nodejs npm
  • 터미널을 종료하여도 웹 서버 실행 유지를 위한 forever 패키지를 설치합니다.
    • $ sudo npm install forever –global
  • 이제 Visual studio 에서 작성하였던 Node.js 웹 서버 코드를 가상 서버에 가져 옵니다.
    • 코드를 가상 서버로 옮기는 방법은 여러가지이나 압축하여 압축파일을 터미널 창으로 옮기면 가상서버로 파일이 복사됩니다.
    • 압축 해제 zip
    • $ sudo apt-get install unzip
    • $ unzip 파일명.zip
  • 압축을 풀고 프로젝트 내 경로로 이동하여 npm 패키지들을 설치해 줍니다.
    • $ sudo npm install
  • app.js 를 실행합니다
    • $ forever start app.js

3. 웹 사이트 접속하기

웹 사이트에 접속해 봅시다.

  • 가상 서버의 외부 IP와 웹 서버의 PORT로 접속 시도
    • 기본 Node.js Express4 프로젝트 생성시 포트번호는 3000
  • http://외부IP:포트번호 로 접속 시도
    • 접속 안됨
  • 가상 서버 SSH 접속 후 포트를 열어준다
    • $ sudo iptables -I INPUT -p tcp –dport 3000 -j ACCEPT
    • 다시 접속 시도
    • 접속 안됨
  • Google Cloud > VPC 네트워크 > 방화벽 규칙
  • 방화벽 규칙 만들기
    • 모든 포트와 IP를 허용하고 확인해 봅니다.
  • 접속 성공

Thanks for reading our blogs.

👋

UART To Network Module

개요

장비를 서버와 연결하여 정보를 주고 받을 수 있도록 하기 위한 module 이다.

Module은 MQTT 와 HTTPS을 이용하여 서버와 통신을 한다.

기본 동작 구성

Board와 Module간의 통신은 UART를 통하여 Data를 주고 받는다. .

Module은 Data Type에 따라 통신 방식(MQTT, HTTPS)을 선택하여 전송한다.

서버에서 명령어는 MQTT를 기반으로 한다.

Arduino 기본 설정

Module의 Main Chip은 ESP8266으로 개발 툴은 Arduino를 이용 합니다.

ESP8266 board 패킷은 Ver2.5를 사용 합니다.

2.5이상부터 HTTPS 및 MQTT 통신의 SSL 인증이 강화 됩니다.

MQTT 적용

AWS 경우 3개의 인증서를 통하여 인증을 하도록 되어 있습니다.

사내 서버의 경우 인증서 없이 USER ID 와 P/W를 통한 인증을 합니다.

하위 version에도 동작 가능하나 HTTPS 인증이 불가능 하여 Ver2.5 사용 합니다.

MQTT 통신을 위한 Library가 따로 있으며 대중적으로 2가지 종류를 사용 합니다.

MQTT and HTTPS 동시 사용시 문제점

ESP8266은 SDK Ver에 따라 <50Kbyte 정도 사용자에게 메모리가 할당 됩니다.

SSL library 사용시 기본 21Kbyte가 소모되어 메모리 overflow 발생 하였습니다.

SSL의 송수신 Buff 사이즈와 MQTT Buff size 변경으로 문제점 수정 하였습니다.

미세먼지 알리미

개요

요즘 미세먼지가 야외 활동을 하는데 주요한 변수가 되었습니다.

사무실 공기 상태를 알려주는 시스템을 만들어 봅니다.

시스템성과 동작

WEMOS D1 R2 보드가 미세먼지 센서와 CO2 센서를 읽어 서버에 전송합니다.

서버는 센서 데이터를 데이터베이스에 저장하며, 하루에 한 번 공기 정보를 알립니다.

또한, 공기 상태가 나쁠 경우 즉각 메일을 발송합니다.

미세먼지 센서

미세먼지 센서는 PMS5003을 사용합니다.

매뉴얼과 소스 코드는 아래 링크를 참고했습니다.

아래는 WEMOS 보드와 PMS5003 센서 연결도입니다.

RX와 RESET은 사용하지 않았습니다.

CO2 센서

CO2 센서는 MH-Z19를 사용합니다.

데이터시트와 소스 코드는 아래 링크를 참고했습니다.

WEMOS 보드와의 연결도입니다.

RX는 사용하지 않았습니다.

SoftwareSerial 사용

SoftwareSerial RX를 사용할 때 동작 중 watchdog reset이 발생하는 문제가 종종 발생합니다.

수신되는 데이터가 많을 경우 자주 발생하므로, WEMOS 보드 두 대를 사용해 미세먼지 센서와 CO2 센서를 하나씩 연결했습니다.

서버 데이터베이스

WEMOS 보드는 센서 정보를 MQTT를 이용해 전송합니다.

MQTT 브로커는 사내 서버를 이용했습니다.

서버는 Firebase를 이용하며, Node.js로 작성했습니다.

Firebase 사용법은 Firebase 페이지에 자세히 설명되어 있으며, 아래 블로그는 정리가 잘 되어 있어 도움이 되었습니다.

Firestorage 저장 문제

Firebase에 deploy 후 Firestorage에 저장이 되지 않는 문제가 있었습니다.

로컬에서 테스트할 때는 잘 동작합니다.

몇 가지 인증 방식을 사용해 테스트를 했는데, 아래 인증 코드를 사용했을 때 정상 동작합니다.

	var config = {
	    apiKey: "xxxxxxxxxxxxxxxxxxxxxx",
	    authDomain: "project_id.firebaseapp.com",
	    databaseURL: "https://project_id.firebaseio.com",
	    projectId: "project_id",
	    storageBucket: "project_id.appspot.com",
	    messagingSenderId: "340030656901"
	};
	firebase.initializeApp(config);
	const db = firebase.firestore();

서버 동작 멈춤

Firebase에 deploy한 서버 코드는 30분간 MQTT 메시지를 수신한 후 센서 정보의 평균을 데이터베이스에 저장합니다.

그런데,  몇 시간 경과된 후 서버가 동작을 하지 않는 문제가 발생했습니다.

테스트를 위해 추가한 http GET API를 호출하면, 동작을 계속합니다.

Firebase에 제약 사항이 있을 것으로 예상되지만, 관련된 문서를 찾지 못했습니다.

문제 개선을 위해 사내 테스트 PC에서 매 분마다 http GET API를 호출하게 했습니다.

알림

알림 메일은 Mailgun을 이용해 발송합니다.

e-mail 10,000개는 무료로 발송할 수 있으며, Cloud platform에서 SMTP port 지원 여부에 상관 없이 사용할 수 있어서 편리합니다.