티스토리 뷰




[IoT-MODLINK] 1. 주제 및 기획

(http://cafe.naver.com/arduinoguide/3601)


[IoT-MODLINK] 2. 도어락 제어하기

(http://cafe.naver.com/arduinoguide/3602)


[IoT-MODLINK] 3. 열림/닫힘 상태 확인하기

(http://cafe.naver.com/arduinoguide/3603)


[IoT-MODLINK] 4. 조명 효과 & 외부 전원 공급하기

(http://cafe.naver.com/arduinoguide/3604)


[IoT-MODLINK] 5. 출입 로그 관리 & 케이스 제작 

(http://cafe.naver.com/arduinoguide/3732)



드디어 마무리 지었습니다!


음... 일단!

원래 계획했던 내용 중에 하나가 모터를 제어해서 금고 기능을 하는 것을 만드는 것이었는데

이 부분이 주제와 맞지 않다고 생각되어 이 부분을 제외하고, RTC모듈로 출입 로그를 넣는 것으로 방향을 바꿨습니다.


다행히 잘 구현되어서 이렇게 완성할 수 있게 됐습니다!

코드 리뷰 > 설치 사진 > 작동 영상 > 후기 순으로 진행하겠습니다.



(1) 코드 리뷰


아무래도 이런 프로젝트 경험이 없을 뿐더러 학생의 코드라

이해하시기 곤란하실 것 같네요.


그래서 대략적인 코드 진행 방향만 설명 해보도록 하겠습니다!


일단 전체 코드와 UI입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
// 헤더 & 네임스페이스
#include <VitconBrokerComm.h>
#include <Adafruit_NeoPixel.h>
#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
using namespace vitcon;
//
 
// 매크로 & 구조체
#define ITEM_COUNT 12
#define DELAY_COUNT 2 // Delay를 사용하는 모듈의 수 
#define HOW_MANY_DATA 100 // 초음파 센서의 표본 데이터의 수 
#define doorRelay 3 
#define trig 4 
#define echo 5 
#define LEDPIN 8 
 
typedef struct {
  bool sts = LOW; 
  unsigned long prev = 0;
}TIMER;  
/* millis()함수를 이용하게 되면 보통 위 구조체에 선언된 2개의 변수가
  필요하기 때문에, 아예 묶어서 구조체로 관리한다.
*/
//
 
 
 
// 전역 변수
 
bool ison; // LED의 점등 여부 확인
char msg[50]="X"// 출입 로그를 저장하는 문자열
int dataCounter; // 초음파 센서의 표본 데이터의 수를 카운팅
int ledValueR,ledValueG,ledValueB; 
float distanceData; // 표본 데이터의 값을 저장하는 변수
char *monthName[12= { 
  "Jan""Feb""Mar""Apr""May""Jun",
  "Jul""Aug""Sep""Oct""Nov""Dec"
};
 
tmElements_t tm; // RTC 모듈에 사용되는 객체
TIMER check[DELAY_COUNT]; // delay가 필요한 모듈의 수 만큼 선언 후 배열로 관리
Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, LEDPIN, NEO_GRB + NEO_KHZ800);
// 60pin LEDstrip을 사용하기 위한 객체
 
 
//
 
// 함수 원형
void myDelay(boolintint);
void isOpen();
void rainbowCycle(bool);
void timeSetting();
uint32_t Wheel(byte);
//
 
// Funtions(from server to device)
void relaySignal(bool val) {  
 
  /* 문 열림 버튼이 눌렸을 때 작동하는 함수 */
 
  digitalWrite(doorRelay, val); 
  if( val == HIGH ) rainbowCycle(val);
}
 
void lightSignal(bool val){
 
  /* LED 토글 버튼이 눌렸을 때 작동하는 함수 */
 
  if(val == HIGH && ison == LOW){
    uint32_t ledValue = strip.Color(ledValueR,ledValueG,ledValueB);
 
    for(int i =0 ; i<strip.numPixels();i++)
      strip.setPixelColor(i,ledValue);
    ison = HIGH;
    strip.show();
  }
  if( val == LOW && ison == HIGH){
    for(int i=0; i<strip.numPixels(); i++)
      strip.setPixelColor(i,0);
    ison = LOW;
    strip.show();
  }
}
 
void ledValueSignalR(int32_t val){
 
  /* RGB 트랙바의 값이 변경될 때 작동하는 함수 */
 
  if( ((int)val) != ledValueR ) ison = LOW;
  ledValueR = (int)val;
}
void ledValueSignalG(int32_t val){
  if( ((int)val) != ledValueG ) ison = LOW;
  ledValueG = (int)val;
}
void ledValueSignalB(int32_t val){
  if( ((int)val) != ledValueB ) ison = LOW;
  ledValueB = (int)val;
}
//
 
//IOTITEM
IOTItemBin relay(relaySignal);
IOTItemBin light(lightSignal);
IOTItemBin doorStatus;
IOTItemBin ledStatus;
IOTItemInt ledValueWriterR(ledValueSignalR);
IOTItemInt ledValueWriterG(ledValueSignalG);
IOTItemInt ledValueWriterB(ledValueSignalB);
IOTItemInt ledValueReaderG;
IOTItemInt ledValueReaderR;
IOTItemInt ledValueReaderB;
IOTItemInt distance;
IOTItemStr str;
 
IOTItem *items[ITEM_COUNT] = { &relay, &doorStatus, &distance,
                               &ledStatus, &light, &ledValueReaderR,
                               &ledValueWriterR, &ledValueReaderG, &ledValueWriterG,
                               &ledValueReaderB, &ledValueWriterB, &str};
const char device_id[] = "3f1e9d56897c741ae09c95f001f5c100";
BrokerComm comm(&Serial, device_id, items, ITEM_COUNT);
//
 
 
 
 
void setup() {
    pinMode(doorRelay, OUTPUT);
    pinMode(trig,OUTPUT);
    pinMode(echo, INPUT);
 
    strip.begin();
    strip.show();
 
    comm.SetInterval(100);
    Serial.begin(250000);
}
 
void loop() {
    isOpen(); /* 초음파 센서를 이용해 열림 확인 */
    ledValueReaderR.Set(ledValueR);
    ledValueReaderG.Set(ledValueG);
    ledValueReaderB.Set(ledValueB); /* 트랙바 값 Setting */
    str.Set(msg); /* 출입 로그 */
    comm.Run();
}
 
 
void myDelay(bool value, int idx, int interval){
 
  /* millis를 이용한 delay기능을 수행하는 함수 */
 
    if ( value ){
      check[idx].prev = millis();
      check[idx].sts = HIGH;
    }
    if( millis() - check[idx].prev >= interval)
      check[idx].sts = LOW;
}
 
void isOpen(){
 
  /*  초음파 센서 값을 이용해 문 열림 확인하는 함수  */
 
  float duration, dist;
 
  if( check[1].sts == LOW ){
    check[1].sts == HIGH;
    digitalWrite(trig,HIGH);
    myDelay(check[1].sts, 11);
  }
 
  if( check[1].sts == HIGH ) return// 아직 딜레이 중
 
  digitalWrite(trig,LOW);
  duration = pulseIn(echo,HIGH);
  dist = ( (float)(340*duration)/10000 ) / 2;
 
  if( dist > 1000 || dist < 1 ) return// 무의미한 센서 값
  
  distanceData += dist;
  dataCounter++;
  if( dataCounter >= HOW_MANY_DATA){
 
    distanceData /= HOW_MANY_DATA;
 
    if( dist >= 10 ){ // 문 열림 감지
      doorStatus.Set(HIGH);
      timeSetting();
    }
    else {// 문 닫혀 있음
      doorStatus.Set(LOW);
    }
    distance.Set(distanceData);
 
    dataCounter = distanceData = 0;
  }
 
}
 
void timeSetting(){
 
  /* 문이 열렸을 경우 출입 로그 업데이트하는 함수  */
 
  char tmp[50= {0};
  msg[0= 0;
  if (RTC.read(tm)) {
    itoa(tm.Day,tmp,10); // 날짜
    strcat(msg,tmp);
    strcat(msg," ");
    strcat(msg,monthName[tm.Month-1]); // 달 이름 변환
    strcat(msg,", ");
 
    if(tm.Hour < 10 ) strcat(msg,"0");
    itoa(tm.Hour,tmp,10); // 시간
    strcat(msg,tmp);
    strcat(msg,":");
 
    if(tm.Minute < 10 ) strcat(msg,"0");
    itoa(tm.Minute,tmp,10); // 분
    strcat(msg,tmp);
    strcat(msg,":");
    
    if(tm.Second < 10 ) strcat(msg,"0");
    itoa(tm.Second,tmp,10); // 초
    strcat(msg,tmp);
  }
 
}
void rainbowCycle(bool val) {
 
  /* 웹/앱을 이용해 문이 열렸을 경우 LED를 밝히는 함수  */
 
  uint16_t i, j;
  uint32_t ledValue = 0;
 
  for(j=0; j<256; j++) {
    for(i=0; i< strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
  }
 
  if( val == HIGH ) digitalWrite(doorRelay, !val);  
 
  if( ison == HIGH )
    ledValue = strip.Color(ledValueR,ledValueG,ledValueB);
 
  for(i=0; i< strip.numPixels(); i++)
    strip.setPixelColor(i, ledValue);
  
  strip.show();
}
 
uint32_t Wheel(byte WheelPos) {
 
  /* LED의 색상을 조정하는 함수 */
 
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return strip.Color(255 - WheelPos * 30, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3255 - WheelPos * 30);
}
cs

<전체 코드>

< UI >




1. Delay처리는?

일단 delay를 사용하지 못하기 때문에 millis()를 활용해야 했습니다.
제 프로젝트에선 약 3개의 모듈에서 delay가 요구됐고, millis() 특성 상 항상 2개의 변수가 필요하다고 생각했습니다.


그래서 millis()를 이용한 myDelay()라는 함수를 만들어 코드의 중복성을 없애고 간편하게 호출함으로써 delay효과를 사용했고,
2개의 변수를 하나의 구조체로 묶어 구조체 변수를 배열로 관리함으로써 index만 바꿔주며 각 모듈에서 delay효과를 쉽게 나타낼 수 있게 하였습니다.

(하지만 strip LED의 수행 속도 때문에 delay를 모두 삭제해서 한 곳에서 밖에 사용하지 않게 됐습니다.)


2. 어떻게 도어락의 열림/닫힘을 제어할까?

도어락의 열림/닫힘 버튼과 릴레이를 연결하여 만약 웹/앱에서 푸쉬 버튼을 누를경우
relaySignal()에서의 val 값이 HIGH가 됩니다.

val 값에 따라 릴레이을 컨트롤하여 버튼과 연결된 부분에 전류를 흘려보내게 됩니다.
버튼에 전류가 전달되면, 도어락은 버튼이 눌렸을 때와 같은 동작을 수행합니다.

그 후 rainbowCycle()이 호출되어 화려한 조명 효과로 문이 열렸음을 알 수 있습니다.


3. 문이 열리는 것을 어떻게 확인할까?


isOpen()이 호출되면
초음파 센서를 통해 문 틀과의 거리를 측정합니다.

센서 값의 정확성을 위해 HOW_MANY_DATA의 값 만큼(100회) 데이터를 수집한 후,
평균 값을 구하고, 이 값이 만약 10cm를 초과할 경우 문이 열렸다고 판단! LED를 점등합니다.

그리고 이 경우 timeSetting()이 호출되어 출입 로그를 현재 시간(날짜, 시간)으로 갱신합니다.






(2) 설치




일단 배선을 위해 도어락을 분해합니다.



긴장되는 순간...  버튼과 점퍼선을 납땜으로 고정시켜 줍시다!





+,- 전선도 알맞게 납땜한 후 사진!



납땜이 마무리되고 다시 조립합니다.



strip LED를 어떻게 꾸밀까 하다가,

 급한대로 빛컨에 대한 저의 사랑을 표현...했습니다



설치 후 자꾸 문제가 발생하여 분해 후 설치를 약 3번 정도 반복하다보니 너무 시간이 많이 흘러

어쩔 수 없이 밤이 되서 완성하게 됐습니다.



(3) 작동 영상



문의 열림/닫힘 동작 시 원래 따르릉 따르릉~ 이 음이 아닌 다른 음이 나오는 것이 정상입니다.

따르릉 음이 나오는 이유는 도어락에 공급되는 전압이 일정 수치보다 낮아졌음을 의미하는데, 이 작동 영상을 촬영할 때

5V 어댑터를 이용해서 그렇습니다.

(집에 남는 어댑터가 19V와 5V밖에 없다보니..)


19V 어댑터로 진행할 경우 정상적인 음이 연주되며 작동하는데, 상대적으로 고전압이라 불안해서 5V로 진행했습니다.

어차피 건전지가 아닌 콘센트를 통해 전기가 공급되므로 큰 상관은 없다고 판단했습니다.


아직 전기에 대한 개념이 약해서 이런 부분에서는 너무 어렵습니다...



4. 후기


MODLINK의 장점은 무엇일까?

많고 많은 하드웨어 보드 중 이 보드가 부각될 수 있는 부분이 어떤 점일까?

이 고민으로부터 프로젝트를 시작했습니다.


MODLINK의 장점은 누구나 손 쉽게 IoT를 구현할 수 있다는 점입니다.

WIFI모듈의 사용 및 서버 구성에 대한 어려움을 덜어주고, 웹/앱에서의 UI를 드래그 앤 드롭으로 간편하게 구성할 수 있습니다.


그래서 생활에 쓰일 수 있는 물건에 적용하자는 생각에서 출발했습니다.


자취를 하는 저는 평소 택배를 시킬 때마다 정말 많이 고민합니다.

옆 집에서 받아줄 리 만무하고 경비실도 없기 때문이죠!

그리고 가끔 도어락 비밀번호를 모르는 부모님 또는 손님이 방문할 때에는 문을 열어주기 위해 집에서 기다려야 했습니다.


이 어려움을 잘 알고있던 지인의 추천으로 프로젝트를 시작했고, MODLINK 덕분에 성공할 수 있었습니다.


사실 컴퓨터 과학을 전공하면서 알고리즘 공부에 매진 해온 저는

하드웨어 쪽에 대한 지식이 많이 없었고, 이런 임베디드 프로그래밍을 해본 적이 많이 없다보니 어려운 점도 많았습니다.

( 모듈 간의 간섭, 당연시 여기던 Delay의 부재, 전류 공급 문제, 잘 되다가 안되는 정!말 이유 모를 문제 등등 )


그래서 그런지 완성에 가까워지니 그 동안 공부했던 내용들 중 예제가 존재하지 않는 주제에 대해 

게시판에 글을 올리고 내가 경험한 내용을 적다보니 정~말 뿌듯했습니다...

댓글! 댓글! 달아주세요! 너무 좋아요....

( UI꾸미기,  MODLINK X RTC module)



운 좋게 선물 받은 MODLINK!

앞으로 꾸준히 사용해서 직접 좋은 결과물 많이 알릴 수 있으면 좋겠습니다.


좋은 추억 만들어주신 VITCON

2차 체험단 여러분 모두


수고하셨습니다. 

그리고

감사합니다!

 

(따뜻한 연말 보내쎄요~)



댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함