방갈로르 같은 혼잡한 도시에서 빈 주차 공간을 찾는 것은 매우 어렵습니다. 자가용으로 외출할 때 가장 먼저 걱정되는 것이 주차 공간입니다. 많은 경우 주차장에 갔더니 만차라서 다른 주차장을 찾아 헤매게 됩니다. 이는 큰 불편이며, 많은 사람들이 차를 가지고 나갈 때 주차 걱정을 합니다.
이 문제를 해결하기 위해 클라우드 기반 스마트 주차 시스템을 제작했으며, 이 시스템이 우리 도시의 주차 문제를 해결하길 바랍니다. ARTIK Cloud는 이러한 작업에 매우 적합한 플랫폼입니다.
이 시스템을 사용하면 모바일이나 웹 앱으로 어디서든 빈 주차 공간을 쉽게 찾을 수 있습니다. 시스템은 30초마다 주차 데이터를 업데이트합니다.
프로젝트 단계
ARTIK Cloud 플랫폼에서 디바이스 생성
ARTIK Cloud에서 애플리케이션 생성
ARTIK Cloud에서 규칙 생성
아두이노 준비
센서 연결
라즈베리 파이 준비
웹 애플리케이션 개발
1단계: ARTIK Cloud에서 새 디바이스 만들기
삼성 계정 로그인 → 개발자 옵션 → 디바이스 유형 → 새로 만들기 → 디바이스 표시 이름 및 고유 이름 입력 → 디바이스 유형 생성 → 새 매니페스트 추가 → 필드 이름(센서 데이터)과 데이터 타입(정수) 지정 → 저장 → 다음: 디바이스 액션 → 액션 생성 또는 표준 액션 선택 → 다음: 활성 매니페스트 → 디바이스 필드 확인 → 완료
내 ARTIK Cloud → 디바이스 → 다른 디바이스 추가 → 생성한 디바이스 선택 → 디바이스 연결 → 디바이스 ID와 디바이스 토큰 기록 (나중에 필요)
2단계: ARTIK Cloud에서 규칙 만들기
규칙 메뉴 → 새 규칙 → 조건(IF) 설정: 디바이스 데이터 필드와 값/조건 선택 → 액션(THEN) 설정: 특정 디바이스의 액션 선택 → 저장
3단계: 아두이노 준비
아두이노의 주 역할은 주차장에 연결된 센서에서 데이터를 수집해 시리얼 포트로 라즈베리 파이에 전송하는 것입니다. 아두이노 메가를 주차장 하나당 하나씩 사용하며, 여러 주차장이면 아두이노 보드를 추가합니다. 초음파 센서를 사용하며 어디에나 설치 가능합니다.
4단계: 센서 연결
모든 센서를 아두이노에 연결하는 방법은 회로도 섹션을 참조하세요.
5단계: 라즈베리 파이 준비
라즈베리 파이 사용 경험이 있다고 가정합니다. 초보자 튜토리얼은 인터넷에 많습니다.
라즈베리 파이는 WebSocket 클라이언트로 작동하며 ARTIK Cloud와 WebSocket 프로토콜로 통신합니다. Node.js로 프로그래밍했습니다(Python과 달리 Node는 라즈베리 파이에 미리 설치되어 있지 않음).
세 단계를 모두 완료하면 라즈베리 파이가 아두이노(시리얼 포트)와 ARTIK Cloud(WebSocket)와 통신할 준비가 됩니다. 주차장 2곳이면 라즈베리 파이 2대가 필요합니다. GitHub에서 소스 코드 다운로드 후 FileZilla 같은 FTP 클라이언트로 라즈베리 파이에 업로드하세요. 소스 코드에서 DEVICE ID와 DEVICE TOKEN을 본인의 것으로 변경하는 것을 잊지 마세요.
실행 명령어
sudo node staff-parking.js
6단계: 웹 애플리케이션 개발
HTML과 JavaScript로 웹 애플리케이션을 개발했습니다. ARTIK Cloud와의 통신에도 WebSocket 프로토콜을 사용했습니다. 지도 연동에는 Google Maps API를 사용했습니다.
GitHub에서 web-app 폴더 다운로드 후 DEVICE ID와 DEVICE TOKEN을 본인의 것으로 수정하세요. 액션을 생성하는 디바이스의 ID를 사용합니다(제 경우 라즈베리 파이). 웹 브라우저에서 websocket.html을 열면 됩니다. 데모 영상을 확인하세요.
코드
라즈베리 파이 (staff-parking) JavaScript
// 주차 데이터 전송
var webSocketUrl = "wss://api.artik.cloud/v1.1/websocket?ack=true";
var device_id = "Your Device ID";
var device_token = "Your Device Token";
var WebSocket = require('ws');
var serialport = require("serialport");
var SerialPort = serialport.SerialPort;
var sp = new SerialPort("/dev/ttyACM0", {
baudrate: 9600,
parser: serialport.parsers.readline("\n")
});
var parking_state = 0;
function getTimeMillis() { return parseInt(Date.now().toString()); }
function start() {
var ws = new WebSocket(webSocketUrl);
ws.on('open', function() {
console.log("WebSocket connection is open ....");
register();
});
ws.on('message', function(data) { handleRcvMsg(data); });
ws.on('close', function() { console.log("WebSocket connection is closed ...."); });
}
function register() {
var registerMessage = '{"type":"register", "sdid":"' + device_id + '", "Authorization":"bearer ' + device_token + '", "cid":"' + getTimeMillis() + '"}';
ws.send(registerMessage, {mask: true});
}
function sendStateToArtikCloud(parking_slot) {
var payload = '{"sdid":"' + device_id + '", "ts":' + getTimeMillis() + ', "data":' + JSON.stringify({parking_slot: parking_slot}) + ', "cid":"' + getTimeMillis() + '"}';
ws.send(payload, {mask: true});
}
start();
sp.on("open", function() {
sp.on('data', function(data) {
var parking_slot = parseInt(data);
sendStateToArtikCloud(parking_slot);
});
});