위즈네트 아카데미

TUTORIAL

튜토리얼

Home  > 튜토리얼

아두이노를 이용한 IoT 주차장 시스템

WIZnet Academy 2016.05.27 16:33 조회 수 : 39

이번 프로젝트는 오픈 하드웨어 플랫폼의 대표주자인 아두이노 플랫폼을 활용하여 가상의 주차장 시스템을 구현해 보겠습니다.
Ethernet은 WIZnet의 W5500 칩을 활용한 이더넷 쉴드를 활용하였습니다. 위즈네트 솔루션의 가장 큰 장점은 TCP/IP 프로토콜이 하드웨어 로직으로 구현되어 있어, TCP/IP 를 자체로 해결해 준다는 것이지요.  따라서 사용자는 TCP/IP 관련 별다른 작업 없이 사용자 어플리케이션 프로그램만 제작하면 됩니다.



이번 프로젝트에 사용된 구성품은 아래와 같습니다.




1. Arduino Uno (or Mega)

2. WIZnet W5500 Ethernet shield

3. Ultra Sonic Sensor(HC-SR04)

4. Servo motor (SG-90)

5. infrared rays sensor (GP2Y0A41SK0F)

6. Bread board

7. Wire

사용했던 센서 및 주요 부품들을 살펴보겠습니다.


1. Ultrasonic Sensor (HC-SR04)

-초음파를 발생시키는 원리는 피에조효과(압전효과*)와 같습니다.

-사용된 초음파 센서는 약 40KHz 정도의주파수를 생성하고, 5~7m정도 까지의 거리를 측정할 수 있습니다.

-이러한 초음파 센서는 거리측정에 사용됩니다.

-초음파센서는 송신부와 수신부로나뉘어져 있으며, 송신된신호가 반사되어 수신부로받아서 이에 대한 시간차를 기반으로 거리를 산출합니다.


-동작은 triger 핀에10us 정도의high 신호를주면 초음파센서는 40kHz 펄스를 자동적으로 8번 발생시킵니다.

-펄스를 발생시킨 직후에 echo핀은high되고반사된 초음파가 감지 되었을 때 echo핀이LOW가됩니다.

-이러한 echo핀이 high되었다가 low가 되는데 걸리는 시간을 측정하여 그 시간을 초음파의 속도/2(왕복이니깐 /2)를 나누면 거리가 나옵니다.

ultra_sonic


-초음파의 속도는 약 340m/s

-초음파 센서의 송신부(triger)에서 일정한 시간의 간격을 둔 짧은초음파 펄스를 방사하고, 대상물에부딪혀 돌아온 펄스를 수신부(echo)에서 받아,이에 대한 시간차를 기반으로 거리를 산출합니다.

-1cm를이동하는데 걸리는 시간은 다음과 같이 구할 수 있습니다.

-따라서,t = 2* 0.01 / 340 = 58.824us로, 초음파가1cm를이동하는데 걸리는 시간은 약 29us가걸립니다.

-거리(cm)= duration(왕복에걸린시간) / 29 / 2(왕복)

ultra_sonci_1

2. 적외선 센서 (GP2Y0A21YK0F)




-적외선 센서는 보통 거리측정, 인체감지로 많이 사용됩니다.

-적외선 센서는 일정 주파수의 빛을 발산하는 발광부,발산하는 빛이 반사되어 감지하는 수광부로이루어져 있습니다.

-적외선 센서는 이러한 반사된 빛을 감지하여 물체의 유,무또는 물체와의 거리를 알 수 있습니다.

-위 소자는 발광부와 수광부가 일체형으로 구성된 소자입니다.

-적외선은 외부 빛에 대한 노이즈를너무 많이 받기 때문에, 실내에서만사용해야한다는 단점이 있습니다.

-그렇기 때문에, 외부에서 거리 측정용으로는 적합하지 않습니다.

-관련된 소자


  • 포토트랜지스터(적외선을 인식하면 전류를 흐르게 한다.)

  • IR Sensor(리모콘)



-이런 적외선 센서는 아날로그 상태일 때 그래프를 보면, 거리가 10cm 이하로떨어질 경우 오히려 전압값이감소합니다.

-그리고 10cm부근에서 최대의 전압값을갖고 있다가 거리가 멀어질 수록 다시 전압이 감소합니다.

-즉, 10cm 이하의 거리측정을 할 때는 오히려 값이 증가하기 때문에 이 센서는 근거리 측정에 부적합합니다.

-데이터 시트에는 10~80cm까지의 거리 측정이 가능하다고 하지만, 주변 환경에 따른 변수가 많아 실질적으로 신뢰할 수 있는 측정 거리는 10~ 40cm정도라 할 수 있습니다.

-아래 타이밍은 최대 38.3ms+9.6ms+5ms이후에 첫 번째 출력이 나온다는 의미입니다.

ir



3.Servo Motor(SG90)
-서보모터는각도를 정밀하게 제어할 수 있는 장점을 이용하여 로보틱스 분야에서 관절부분에 많이 사용되고 있습니다.

-0-180도or 360도까지 각도제어가 가능합니다.

-즉, 정해진 명령에 따라 정확한 위치와 속도를 맞출 수 있는 모터를 서보모터라합니다.

-서보모터는PWM(Pulse Width Modulation)을 사용하여 펄스의 폭을 조절하여 제어하게 됩니다.

-이 때 각도를 제어함에 있어서 아래 그림의 Duty Cycle의길이를 조정함으로써 제어를 할 수 있게 됩니다.

-사용한 SG90은 한 주기가 20ms로 정해져 있으며,이 한 주기 내에 Duty Cycle이 얼마나 되느냐에 따라 움직이는 각도가 달라집니다.

-아두이노에서서보모터를제어하는 방법으로는 2가지가있습니다.



  • 직접 각도를 입력하는 방법

  • 사용자가 직접 DutyCycle 폭을 제어하는 방법


servo_motor

위의 센서들을 조합하여 아래와 같이 회로도를 구성합니다.

circuit

아래와 같이 모형을 만들었습니다. (아직은 조금 허접합니다^^)

parking

서보모터는 출입문을 열고 닫는데 사용됩니다.

servo-park








 

초음파 센서를 이용해서 차량이 왔는지 확인하고,  LED를 이용해서 문이 OPEN되었는지 CLOSE 되었는지 확인할 수 있습니다.

ultra_parking

적외선 센서를 사용하면 차량의 주차 여부를 확인할 수 있습니다.

ir_parking

이 모든 것을 웹을 통해 확인이 가능합니다. 아래는 아두이노에 구축한 웹서버에 접속한 화면입니다.

arduino_web

아래는 이번 프로젝트를 위해 짠 코드 내용입니다. 중간에 각 부분의 코드 내용에 대한 설명이 포함되어 있어서, 내용 확인이 바로 되실 겁니다~

[code language="c"]



/*
Parking System with Ethernet(Web Server) in Arduino

created 25 Mar 2016
by Jinhee Ahn
*/

#include <SPI.h>
#include <Ethernet.h>
#include <Servo.h>

#if defined(WIZ550io_WITH_MACADDRESS) // WIZ550io는 MAC주소가 내장되어 있는 모듈입니다.
;
#else
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // mac 주소 입력
#endif

//#define __USE_DHCP__ // DHCP사용 시 Define

// Static IP 설정
IPAddress ip(192,168,1,177); // 로컬 네트워크에서 독립적인 IP를 사용해야 합니다.
IPAddress gateway( 192, 168, 1, 1 );
IPAddress subnet( 255, 255, 255, 0 );
// fill in your Domain Name Server address here:
IPAddress myDns(8, 8, 8, 8); // google puble dns

// 소켓을 80번 포트로 생성
// 위 함수로 Socket을 생성하고 클라이언트 접속을 기다리는 대기모드로 진입한다.
// HTTP를 사용할 땐 기본적으로 80번 포트 사용.
// IANA(Internet Assigned Numbers Authority)에서 HTTP는 포트를 80번을 사용하자고 권유하였음.
// 하지만 사용자가 원하는 대로 포트 설정 가능하다.
// https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt
EthernetServer server(80);

void check_led_status();

#define close_led 5
#define open_led 3
#define trig 8 // 초음파센서의 송신부를 8번핀으로 설정합니다.
#define echo 9 // 초음파센서의 수신부를 9번핀으로 설정합니다.

// 적외선 센서
int pinNum = A1; //아날로그 0번핀 사용
int distance = 0; //거리값 변수 생성
// 초음파 센서
float distance2 = 0; //거리값 변수 생성

Servo servo;

void setup() {
servo.attach(7);
Serial.begin(9600);
pinMode(trig, OUTPUT);
pinMode(echo, INPUT);
pinMode(close_led, OUTPUT);
pinMode(open_led, OUTPUT);
pinMode(pinNum, INPUT); //A1번핀을 INPUT으로 설정
servo.write(95);
digitalWrite(close_led, LOW);
digitalWrite(open_led, HIGH);
while (!Serial) {
; // 시리얼 포트가 연결 될 때까지 기다림, 레오나르도 보드의 경우만에만 필요
}

// 이더넷 디바이스 초기화
#if defined __USE_DHCP__ // DHCP 사용 시 (네트워크 정보 DHCP에서 할당받음)
#if defined(WIZ550io_WITH_MACADDRESS) // WIZ550io에 할당된 MAC 주소를 사용할 경우
Ethernet.begin();
#else
Ethernet.begin(mac); // MAC이 없는 이더넷 디바이스의 경우
#endif
#else // Static 주소로 사용 시 (네트워크 정보 사용자가 임의로 지정)
#if defined(WIZ550io_WITH_MACADDRESS)
Ethernet.begin(ip, myDns, gateway, subnet);
#else
Ethernet.begin(mac, ip, myDns, gateway, subnet);
#endif
#endif

// 서버 연결 시작
server.begin();
Serial.println("Parking Control System");
Serial.print("server is at ");
Serial.println(Ethernet.localIP()); // 설정된 IP 시리얼 메세지로 출력
}

void loop() {
EthernetClient client = server.available(); // 클라이언트로 부터 받은 Data "size"를 리턴
// loop에서 반복문으로 돌면서 클라이언트가 연결될 때 까지 대기
if (client) { // 클라이언트와 연결되면 '1'이 된다. (칩 내부적으로 ESTABLISHED 상태가 된다) 그리고 Data "Size"를 읽는다.
Serial.println("new client");
boolean currentLineIsBlank = true;
String buffer = ""; // 문자열 버퍼 선언
// 예외처리를 위한 서버의 현재 소켓 상태를 읽는다.
// LISTEN, CLOSE, FIN_WAIT, CLOSE_WAIT 상태라면 Connection이라 판단하지 않는다.
while (client.connected()) {

// 적외선
int data = analogRead(pinNum); //적외선 거리 센서로 부터 측정값을 읽어온다
int volt = map(data, 0, 1023, 0, 5000); //측정한 volt값을 0에서 5000사이의 값으로 변환
distance = (21.61/(volt-0.1696))*1000; //측정값을 통해 거리를 계산

long start = millis();

// 초음파 센서는 송신부와 수신부로 나뉘어 있으며,
// 송신부터 수신까지의 시간을 기준으로 거리를 측정합니다.
// 트리거로 연결된 핀이 송신부를 담당하며, 에코로 연결된 핀이 수신부를 담당합니다.
// 송신부에서 2마이크로초 정도 또는 그 이상의 시간동안 초음파를 발생시킵니다.
// 초음파 발생 전후로, 잡음을 제거하기 위하여 전류를 보내지 않도록 설정합니다.
digitalWrite(trig, LOW);
digitalWrite(echo, LOW);
delayMicroseconds(2);
digitalWrite(trig, HIGH);
delayMicroseconds(10);
digitalWrite(trig, LOW);

// 수신부의 초기 로직레벨을 HIGH로 설정하고, 반사된 초음파에 의하여 ROW 레벨로 바뀌기 전까지의 시간을 측정합니다.
// 단위는 마이크로 초입니다.
unsigned long duration = pulseIn(echo, HIGH);

// 초음파의 속도는 초당 340미터를 이동하거나, 29마이크로초 당 1센치를 이동합니다.
// 따라서, 초음파의 이동 거리 = duration(왕복에 걸린시간) / 29 / 2 입니다.
distance2 = duration / 29.0 / 2.0;

if (client.available()) { // 클라이언트로 부터 수신된 데이터가 있다면
char c = client.read(); // 데이터를 1byte씩 읽어서 c 변수에 저장
buffer += c; // 1 byte 데이터를 문자열 버퍼에 저장
Serial.write(c); // 클라이언트로 부터 수신한 데이터 시리얼로 출력

if (c == 'n' && currentLineIsBlank) { // currentLineIsBlank이 True일 때
// 클라이언트로 부터 HTTP 요청이 빈 라인(n)으로 끝났을 때, 표준 HTTP 응답 헤더 전송 시작
// HTTP 시작
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close");
client.println("Refresh: 5");
client.println();

// 서버에 지정되어 있는 데이터 클라이언트로 전송
// client.println("<!DOCTYPE HTML>"); // HTML5 사용시
// HTML 시작
client.println("<html>"); // HTML 언어로 코드 작성
client.println("<body>");

if (distance > 10) {
client.println("Parking is <font color='blue'>Empty</font>");
}
else {
client.println("Parking is <font color='red'>Full</font>");
}

client.println("<br />");
client.println("<br />");

if(distance2 > 7){
client.println("<font color='red'>There is not a car in front of door !!</font>");
}
else if (distance2 < 7) {
client.println("<font color='green'>There is a car in front of door !!.</font>");
}

client.println("<br />");
client.println("<br />");

if (digitalRead(open_led) == LOW) { // LED PORT가 '1'이면 LED ON, 아니면 LED OFF
client.println("<font color='blue'>Door is open</font>"); // LED ON
}
else {
client.println("<font color='red'>Door is close</font>"); // LED OFF
}
client.println("<br />");
client.println("<FORM method="get" action="/parking.htm">");
client.println("<P> <INPUT type="radio" name="status" value="1">Door is OPEN");
client.println("<P> <INPUT type="radio" name="status" value="0">Door is CLOSE");
client.println("<P> <INPUT type="submit" value="Submit"> </FORM>");
client.println("</body>");
client.println("</html>");
/*
if(distance2 > 20){
servo.write(0);
digitalWrite(close_led, LOW);
digitalWrite(open_led, HIGH);
}
else if (distance2 < 20) {
servo.write(90);
digitalWrite(close_led, HIGH);
digitalWrite(open_led, LOW);
}*/
// HTML 종료
break;
}
if (c == 'n') { // 클라이언트로 부터 빈 라인을 받으면 버퍼 Clear
currentLineIsBlank = true;
buffer="";
}
// LED ON
else if ( c == 'r') { // 빈 라인이 아닌 "GET /led.cgi?status=1" 데이터를 받으면
if(buffer.indexOf("GET /parking.htm?status=1")>=0){
// HTTP
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println();
// HTML
client.println("<html>");
client.println("<body>");

if(distance2 > 20){
client.println("<font color='red'>There is not a car in front of door.</font>");
}
else if (distance2 < 20) {
client.println("<font color='green'>There is a car in front of door.</font>");
}

client.println("<br />");
client.println("<a href="/parking.htm">Go to control-page</a>"); // Main 페이지로 리턴
client.println("</body>");
client.println("</html>");
servo.write(10);
digitalWrite(close_led, HIGH);
digitalWrite(open_led, LOW);
// HTML DONE
currentLineIsBlank = false;
break;
}

// LED OFF
if(buffer.indexOf("GET /parking.htm?status=0")>=0){ // "GET /led.cgi?status=0" 데이터를 받으면
// HTTP
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println();
// HTML
client.println("<html>");
client.println("<body>");

if(distance2 > 20){
client.println("<font color='red'>There is not a car in front of door.</font>");
}
else if (distance2 < 20) {
client.println("<font color='green'>There is a car in front of door.</font>");
}

client.println("<br />");
client.println("<a href="/parking.htm">Go to control-page</a>"); // Main 페이지로 리턴
client.println("</body>");
client.println("</html>");
servo.write(95);
digitalWrite(close_led, LOW);
digitalWrite(open_led, HIGH);
// HTML DONE
currentLineIsBlank = false;
break;
}
}
// r과 n이 아니라면 아직 전송 받을 Data가 남아있다는 뜻으로 알고 서버는 데이터를 기다린다.
else{ //if( c != 'r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}

delay(1);
client.stop(); // TCP Connection 종료
Serial.println("client disonnected");
}
}


[/code]




글출처 : Edward's Lab