-
라즈베리파이, 초음파 센서를 이용한 출입 감지 (2)Raspberry Pi (deprecated) 2016. 6. 7. 15:00
(1) 에 이어서 이번엔 초음파 센서를 사용할 코드를 짜보자.
본 코드는 node.js 로 만들었기 때문에 node.js 가 설치되어 있어야 한다.
Raspbian 에서 node.js 를 설치하는 법은 https://blog.wia.io/installing-node-js-v4-0-0-on-a-raspberry-pi 를
참고해서 설치하도록 하자.
(코드가 많아서 적기 귀찮아 스샷으로 대체 함....ㅠ)
설치가 되었다면 간단한 모듈화를 위해 먼저 초음파 센서 알고리즘 코드를 넣을 폴더를 만들자.
본인은 wave_distance 라는 폴더를 만들고 그 안에 wave_distance.js 라는 파일을 만들었다.
코드를 작성하기전 외부 모듈의 도움을 받으면 조금 더 손쉽게 코드를 작성할 수 있는데 본인이 받은
모듈은 wiring-pi , microtime-nodejs , color 이다.
wiring-pi 같은 경우 연결된 센서를 좀 더 손쉽게 제어할 수 있도록 코드를 집합시켜 놓은 모듈이고
microtime-nodejs 같은 경우 밀리세컨드를 측정하기 위한 모듈이고 ,colors 같은 경우 콘솔창에서
글자에 색깔을 입히는 기능이 있는데 , 본인은 그냥 좀 더 결과를 이쁘고(?) 편하게 볼려고 받은 모듈이다.
//Module exports module.exports = function() { //Module load var wpi = require('wiring-pi'); var microt = require('microtime-nodejs'); var TransPost = require('../Trans/PostTrans'); var TRIG, ECHO; // pin number TRIG = 5; ECHO = 4; //Sensor module setup //Gpio setup //General Purpose IO wpi.setup('gpio'); //gpio on the raspberry-pi wpi.wiringPiSetup(); // TRIG pin will be used to send the signal wpi.pinMode(TRIG, wpi.OUTPUT); // ECHO pin will be used to listen for returning signal. wpi.pinMode(ECHO, wpi.INPUT);
먼저 라즈베리파이에 초음파 센서를 사용하기 위한 셋팅코드를 보자.
해당 코드 전체를 나중에 메인파일에서 불러 쓰기 위해 exports 시켰다.
wpi , microt 에 wiring-pi 와 microtimr-nodejs 를 require 시키고 TransPost 같은 경우 측정한 데이터를
post 방식으로 보내기 위해 작성한 코드를 require 시키는데 아직 전송 코드는 작성하지 않았기 때문에
작동하지 않을 것 이다.
TRIG 와 ECHO 변수를 만들고 각 변수에 (1)에서 연결했던 핀넘버를 적용시킨다.
(wiring-pi 의 4 번은 GPIO23 이고 5번은 GPIO24를 가르킨다.
(1) 글의 GPIO 표를 보면 GPIO_GEN 이라고 번호가 있을 것 이다. 그 번호를 적어주면 된다.)
wpi.setup 메소드로 wiring-pi 의 설정을 gpio 설정하고 wiringPiSetup() 메소드로 적용시킨다.
pinMode 같은 경우 2개의 인자를 받는데 TRIG 같은 경우 신호를 내보내고, ECHO 는 신호를 받는다.
pinMode에 TRIG 는 OUTPUT 으로 설정하고 ECHO 는 INPUT 으로 설정한다.
function sleep(millseconds) { var start = new Date().getTime(); for (var i = 0; i < 1e7; i++) { if((new Date().getTime() - start) > millseconds) { break; } } }
위 코드는 sleep 함수다. 일시적으로 현재 프로세스를 멈추기 위해 만든 함수이다.
function checkDistance() { wpi.digitalWrite(TRIG, wpi.LOW); sleep(0.2); wpi.digitalWrite(TRIG, wpi.HIGH); sleep(2); wpi.digitalWrite(TRIG, wpi.LOW); while(wpi.digitalRead(ECHO) == wpi.LOW); var startTime = microt.now(); while(wpi.digitalRead(ECHO) == wpi.HIGH); var endTime = microt.now(); var distance = (endTime - startTime) / 58; return Math.ceil(distance); }
checkDistance 같은 경우 초음파 센서를 이용한 현재 거리를 재는 함수이다.
먼저 digitalWrite 메소드로 Trig 에 Low, High , Low 를 신호를 주어서 초음파 센서의 트리거를 완성시킨다.
(Trig 는 신호를 내보내기 때문에 LOW = false (시작점) -> HIGH = true (끝 점) -> LOW = false (시작 점) )
TRIG의 신호가 나갔다 들어오는 트리거를 설정 해줘야 제대로 동작을 한다.
이제 while 문으로 TRIG 의 신호를 받기 전의 ECHO 시작 시간을 얻어내고 startTime 에 밀리세컨드로 저장한다.
그리고 TRIG 의 신호를 받은 ECHO 시간을 얻어내서 endTime 에 밀리세컨드로 저장한다.
이제 현재의 거리를 재기 위해 ( endTime - startTime ) / 58 을 한 뒤 distance 에 현재 거리를 저장한다.
(위 ( endTime - startTime ) / 58 의 공식은 해당 초음파 센서의 공식 메뉴얼에 거리를 재기 위한 식으로 나와있다.)
이제 알아보기 쉽게 Math.ceil 함수로 소숫점을 자른 뒤 distance 를 return 한다.
function getTime() { var now = new Date(); //getMonth() (0~11) var nowAll = now.getFullYear() + "-" + (now.getMonth() + 1) + "-" + now.getDate() + " " + now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds() + " "; return nowAll; }
getTime 함수는 현재 시간을 return 하는 함수이다. 나중에 결과를 보낼 때 현재 시간을 함께 보내기 위해
만든 함수이다. getMonth 같은 경우 0~11 을 return 하기 때문에 + 1 을 시켜서 1~12 로 맞추었다.
function StartDistance() { var disArray = new Array(6); var DisTotal = 0; for(var i = 0 ; i < 6 ; i++) { disArray[i] = checkDistance(); DisTotal += disArray[i]; } var AvrDistance = Math.ceil(DisTotal / 6);
StartDistance 함수는 최종 결과를 return 하는 함수 이다.
먼저 현재 평균거리를 얻기위해 disArray 배열을 만들고 DistTotal 변수를 만든다.
for 문으로 초음파 센서가 작동을 시작하고 최초 6번의 거리를 배열에 각각 넣고 배열 안의 수치를 모두 더해
DisTotal 에 넣는다.
그런 뒤 DisTotal을 6번으로 나누어 AvrDistance 에 대입해서 현재 초음파 센서의 기준 거리를 만든다.
setInterval(function() { var Msg = checkDistance(); var Time = getTime(); console.log('DISTANCE = '.yellow + Msg + ' Cm'.yellow + ' ' + Time); var AvrDistanceDiv = AvrDistance / 6; if(Msg <= (AvrDistance - AvrDistanceDiv) ) { var nowDistance = new Array(); var inTotal = 0; var outTotal = 0; var i = 0; while(true) { nowDistance[i] = checkDistance(); i++; if(checkDistance() >= AvrDistance) { break; } }
그런 뒤 setInterval 함수를 사용하여 0.1초 마다 현재 상황을 체크 할 수 있도록 하고 내부에서는 최종 결과를
만들어 낼 것 이다.
먼저 콘솔에 수치를 나타내기 위해 Msg 에 checkDistance() 함수를 사용하고 Time 에는 getTime() 함수를
사용한다. console.log 로 현재 거리와 시간을 계속 나타낸다.
AvrDistanceDiv 는 지나가는 물체 또는 사람을 감지 하는 기준이 되는데 위에서 구했던 평균거리를 /6 한 뒤
if 문에서 Msg 즉 , 현재 거리가 평균거리 - (평균거리/6) 이 되면 사람이 지나간다고 인식하게 만든다.
그런 뒤 출입을 체크하기 위해 nowDistance 라는 배열을 만들고 inTotal 과 outTotal 역시 선언한다.
그리고 nowDistance 의 배열을 셀 i 변수 역시 만든다.
while 문을 사용하여 nowDistance 배열에 현재 거리를 계속 넣는다.
if문을 써서 현재 거리가 처음 만들었던 기준거리 AvrDistance 와 같거나 높아지면 현재 센서에 감지되는 것 이
없다는 뜻이 되기에 그 때 while 문을 빠져 나온다.
for(var i = 0; i < nowDistance.length-1 ; i++) { if(nowDistance[i] < nowDistance[i+1]) { inTotal++; } else { outTotal++; } } if(inTotal > outTotal) { TransPost('Out',Time); console.log('Out Trans'.red); sleep(3000); } else if(outTotal > inTotal) { TransPost('In', Time); console.log('In Trans'.green); sleep(3000); } } },100); } StartDistance(); }
그런 뒤 for 문을 써서 감지되었을 때의 거리가 들어있는 배열인 nowDistance 배열을 탐색하며 차례차례 비교
하며 수치에 따라 inTotal 과 outTotal 을 증가시킨다.
최종적으로 inTotal 과 outTotal 의 갯수를 세서 if문을 사용하여 In & Out 의 결과를 반환한다.
TransPost는 Post로 결과를 보내는 모듈로서 초기에 2개의 인자를 받아 보내도록 만들어 두었고
console.log 로 in 인지 out 인지 출력하게 된다.
( IN 과 OUT 의 결과는 센서를 어떤 방향으로 설치 했는지에 따라 바뀔 수 있다. )
위 코드들을 합쳐 전체 코드를 보자.
//Module exports module.exports = function() { //Module load var wpi = require('wiring-pi'); var microt = require('microtime-nodejs'); var TransPost = require('../Trans/PostTrans'); var TRIG, ECHO; // pin number TRIG = 5; ECHO = 4; //Sensor module setup //Gpio setup //General Purpose IO wpi.setup('gpio'); //gpio on the raspberry-pi wpi.wiringPiSetup(); // TRIG pin will be used to send the signal wpi.pinMode(TRIG, wpi.OUTPUT); // ECHO pin will be used to listen for returning signal. wpi.pinMode(ECHO, wpi.INPUT); //Process Sleep function function sleep(millseconds) { var start = new Date().getTime(); for (var i = 0; i < 1e7; i++) { if((new Date().getTime() - start) > millseconds) { break; } } } function checkDistance() { wpi.digitalWrite(TRIG, wpi.LOW); sleep(0.2); wpi.digitalWrite(TRIG, wpi.HIGH); sleep(2); wpi.digitalWrite(TRIG, wpi.LOW); while(wpi.digitalRead(ECHO) == wpi.LOW); var startTime = microt.now(); while(wpi.digitalRead(ECHO) == wpi.HIGH); var endTime = microt.now(); var distance = (endTime - startTime) / 58; return Math.ceil(distance); } function getTime() { var now = new Date(); //getMonth() (0~11) var nowAll = now.getFullYear() + "-" + (now.getMonth() + 1) + "-" + now.getDate() + " " + now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds() + " "; return nowAll; } function StartDistance() { var disArray = new Array(6); var DisTotal = 0; for(var i = 0 ; i < 6 ; i++) { disArray[i] = checkDistance(); DisTotal += disArray[i]; } var AvrDistance = Math.ceil(DisTotal / 6); setInterval(function() { var Msg = checkDistance(); var Time = getTime(); console.log('DISTANCE = '.yellow + Msg + ' Cm'.yellow + ' ' + Time); var AvrDistanceDiv = AvrDistance / 6; if(Msg <= (AvrDistance - AvrDistanceDiv) ) { var nowDistance = new Array(); var inTotal = 0; var outTotal = 0; var i = 0; while(true) { nowDistance[i] = checkDistance(); i++; if(checkDistance() >= AvrDistance) { break; } } for(var i = 0; i < nowDistance.length-1 ; i++) { if(nowDistance[i] < nowDistance[i+1]) { inTotal++; } else { outTotal++; } } if(inTotal > outTotal) { TransPost('Out',Time); console.log('Out Trans'.red); sleep(3000); } else if(outTotal > inTotal) { TransPost('In', Time); console.log('In Trans'.green); sleep(3000); } } },100); } StartDistance(); }
알고리즘을 만들었으니 이제 Post 로 보내는 코드를 만들자.
먼저 wave_distance 폴더를 빠져나와서 새롭게 Trans 폴더를 만들고 그 안에 PostTrans.js 파일을 만들자.
module.exports = function(detected,time) { var request = require('request'); request ( url: 보낼주소 , qs : { "data1" : detected , "data2" : time }, method : 'POST'}, function(err, res, body) { if(err) { return console.log('Error: '.red + err); } if(res.statusCode !== 200) { return console.log('Err! res.statusCode : '.red + res.statusCode ); } } ) };
PostTrans 는 단순히 결과를 받아서 전송하는 모듈이다.
먼저 다른 코드에서 가져다 쓰기 위해 exports 시키고 인자로는 결과와 시간 , 2개의 인자를 받는다.
전송을 도와 줄 request 모듈을 require 시킨 뒤에 코드를 작성하자.
url 에는 보낼 주소를 적고 JSON 방식으로 data1 에는 결과를 , data2 에는 시간을 넣고 method 는 POST로 설정을
하였고, 콜백함수로 err 가 생겼을 때 콘솔로 출력하게 만들었다.
이제 마지막으로 메인 파일을 작성하자.
Trans 폴더를 빠져나와서 StartApp.js 파일을 만들자.
var WaveDistance = require("./Wave_distance/wave_distance"); var colors = require('colors'); WaveDistance();
아주 심플하다. wave_distance 코드를 require 시키고 colors 모듈을 가져온 뒤 WaveDistance 를
실행 하면 끝이다.
급하게 만든거라 어설픈 부분이 있기에 바꿀 수 있는 부문이 있다면 직접 바꿔가며 좀 더 효율적으로
만들어 보길 바란다.
실행 했을 시의 화면이다. 거리 값이 한번씩 튀기는 하지만 나름 일정하게 측정을 한다.
실제 케이스를 씌워서 연결한 모습이다.
이것 외에도 활용도가 무궁무진하기 때문에 취미로 한번씩 만져주기에 재미있는 장난감이다.
(끝)
'Raspberry Pi (deprecated)' 카테고리의 다른 글
라즈베리파이, 초음파 센서를 이용한 출입 감지 (1) (1) 2016.06.07