Skip to content

Commit 6a745a9

Browse files
authored
Merge pull request #830 from entrylabs/develop-hw
Develop hw
2 parents 6c20312 + 3c21b33 commit 6a745a9

File tree

8 files changed

+877
-24
lines changed

8 files changed

+877
-24
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,399 @@
1+
#include <Servo.h>
2+
#include <DHT.h>
3+
#include <math.h>
4+
5+
// 초음파 센서 핀 정의
6+
#define ULTRASONIC_TRIG_PIN A0 // 트리거 핀: A0
7+
#define ULTRASONIC_ECHO_PIN A1 // 에코 핀: A1
8+
9+
// DHT11 센서 핀 정의
10+
// 온습도 센서는 무조건 a2(analog channel 2)에 연결되어야 함
11+
#define DHTPIN A2
12+
#define DHTTYPE DHT11
13+
DHT dht(DHTPIN, DHTTYPE);
14+
15+
char remainData;
16+
Servo servoMotor; // 서보 객체 생성
17+
18+
// 서보 모터 핀 번호 (디지털 핀 2)
19+
const int SERVO_MOTOR = 2;
20+
21+
// 온습도 센서(DHT11) 데이터 갱신 주기 관련 전역 변수
22+
unsigned long lastDHTTime = 0;
23+
uint16_t lastDHTValue = 0;
24+
25+
// ─────────────────────────────────────────────
26+
// 자동문(비동기) 상태 관리용 전역 변수
27+
// ─────────────────────────────────────────────
28+
bool autoDoorActive = false; // 자동문 동작 중 여부
29+
int autoDoorPhase = 0; // 자동문 동작 단계
30+
unsigned long autoDoorLastTime = 0; // 마지막 상태 전환 시각 (millis())
31+
32+
// ─────────────────────────────────────────────
33+
// 초음파 센서 관련 전역 변수 (측정 주기 제한용)
34+
// ─────────────────────────────────────────────
35+
unsigned long lastUltrasonicTime = 0;
36+
uint16_t lastUltrasonicValue = 0;
37+
38+
// ─────────────────────────────────────────────
39+
// 초음파 센서 측정 함수 (cm 단위)
40+
// ─────────────────────────────────────────────
41+
uint16_t readUltrasonicSensor()
42+
{
43+
long duration;
44+
45+
// 트리거 핀 LOW로 안정화
46+
digitalWrite(ULTRASONIC_TRIG_PIN, LOW);
47+
delayMicroseconds(2);
48+
49+
// 트리거 핀을 HIGH로 10us 동안 유지하여 초음파 발사
50+
digitalWrite(ULTRASONIC_TRIG_PIN, HIGH);
51+
delayMicroseconds(10);
52+
digitalWrite(ULTRASONIC_TRIG_PIN, LOW);
53+
54+
// 에코 핀에서 HIGH 펄스의 길이 측정 (타임아웃 30000us)
55+
duration = pulseIn(ULTRASONIC_ECHO_PIN, HIGH, 30000);
56+
if (duration == 0)
57+
{
58+
// 측정 실패 시 오류값 999 반환
59+
return 999;
60+
}
61+
62+
// 거리(cm) = (duration * 0.034) / 2
63+
uint16_t distance = duration * 0.034 / 2;
64+
return distance;
65+
}
66+
67+
// ─────────────────────────────────────────────
68+
// setup()
69+
// ─────────────────────────────────────────────
70+
void setup()
71+
{
72+
Serial.begin(9600);
73+
Serial.flush();
74+
75+
// 초음파 센서 핀 모드 설정
76+
pinMode(ULTRASONIC_TRIG_PIN, OUTPUT);
77+
pinMode(ULTRASONIC_ECHO_PIN, INPUT);
78+
79+
initPorts();
80+
81+
// DHT 센서 초기화 (DHT 센서는 A2에 연결됨)
82+
dht.begin();
83+
84+
// 서보 모터 초기화: 디지털 핀 2에 연결 후 기본값 90도로 설정 (멈춤 상태)
85+
servoMotor.attach(SERVO_MOTOR);
86+
servoMotor.write(90);
87+
88+
delay(200);
89+
}
90+
91+
// ─────────────────────────────────────────────
92+
// initPorts()
93+
// - 서보 모터 핀는 제외하고 초기화 (DHT 센서는 아날로그 채널이므로 디지털 초기화 대상 아님)
94+
// ─────────────────────────────────────────────
95+
void initPorts()
96+
{
97+
for (int pinNumber = 0; pinNumber < 14; pinNumber++)
98+
{
99+
if (pinNumber == SERVO_MOTOR)
100+
{
101+
continue; // 서보 모터 핀은 건너뜀
102+
}
103+
pinMode(pinNumber, OUTPUT);
104+
digitalWrite(pinNumber, LOW);
105+
}
106+
}
107+
108+
// ─────────────────────────────────────────────
109+
// loop()
110+
// - 시리얼 수신 → updateDigitalPort()로 처리
111+
// - 자동문 상태 업데이트 (non-blocking)
112+
// - pinValues 전송
113+
// ─────────────────────────────────────────────
114+
void loop()
115+
{
116+
// 시리얼에 데이터가 있으면 읽어 처리
117+
while (Serial.available())
118+
{
119+
char c = Serial.read();
120+
updateDigitalPort(c);
121+
}
122+
123+
// 자동문 상태 머신 업데이트
124+
if (autoDoorActive)
125+
{
126+
updateAutoDoor();
127+
}
128+
129+
delay(15);
130+
sendPinValues();
131+
delay(10);
132+
}
133+
134+
// ─────────────────────────────────────────────
135+
// updateAutoDoor()
136+
// - 비동기로 LED와 서보 모터를 동시에 제어
137+
// ─────────────────────────────────────────────
138+
void updateAutoDoor()
139+
{
140+
unsigned long now = millis();
141+
142+
switch (autoDoorPhase)
143+
{
144+
case 1:
145+
if (now - autoDoorLastTime >= 1300)
146+
{
147+
servoMotor.write(90); // 서보 멈춤
148+
autoDoorPhase = 2;
149+
autoDoorLastTime = now;
150+
}
151+
break;
152+
153+
case 2:
154+
if (now - autoDoorLastTime >= 10000)
155+
{
156+
servoMotor.write(180); // 반대 방향 회전 시작
157+
autoDoorPhase = 3;
158+
autoDoorLastTime = now;
159+
}
160+
break;
161+
162+
case 3:
163+
if (now - autoDoorLastTime >= 1100)
164+
{
165+
servoMotor.write(90); // 서보 정지
166+
autoDoorActive = false;
167+
autoDoorPhase = 0;
168+
}
169+
break;
170+
}
171+
}
172+
173+
// ─────────────────────────────────────────────
174+
// updateDigitalPort()
175+
// - 시리얼로 수신된 명령(디지털/아날로그)을 해석해 핀에 반영
176+
// ─────────────────────────────────────────────
177+
void updateDigitalPort(char c)
178+
{
179+
if (c >> 7)
180+
{
181+
// 상위 비트가 1인 경우
182+
if ((c >> 6) & 1)
183+
{
184+
// 디지털 쓰기
185+
if ((c >> 5) & 1)
186+
{
187+
int port = (c >> 1) & B1111;
188+
if (port == SERVO_MOTOR)
189+
{
190+
// 서보 제어는 아래 아날로그 방식으로 처리
191+
}
192+
else
193+
{
194+
setPortWritable(port);
195+
if (c & 1)
196+
{
197+
digitalWrite(port, HIGH);
198+
}
199+
else
200+
{
201+
digitalWrite(port, LOW);
202+
}
203+
}
204+
}
205+
else
206+
{
207+
remainData = c;
208+
}
209+
}
210+
else
211+
{
212+
// 디지털 읽기
213+
int port = (c >> 1) & B1111;
214+
setPortReadable(port);
215+
}
216+
}
217+
else
218+
{
219+
// 아날로그 값 처리
220+
int port = (remainData >> 1) & B1111;
221+
int value = ((remainData & 1) << 7) + (c & B1111111);
222+
223+
if (port == SERVO_MOTOR)
224+
{
225+
setPortWritable(port);
226+
if (value == 1)
227+
{
228+
// open_door
229+
servoMotor.write(0);
230+
delay(1300);
231+
servoMotor.write(90);
232+
}
233+
else if (value == 2)
234+
{
235+
// close_door
236+
servoMotor.write(180);
237+
delay(1100);
238+
servoMotor.write(90);
239+
}
240+
else if (value == 3)
241+
{
242+
// auto_door (blocking 방식)
243+
servoMotor.write(0);
244+
delay(1300);
245+
servoMotor.write(90);
246+
delay(2000);
247+
servoMotor.write(180);
248+
delay(1100);
249+
servoMotor.write(90);
250+
}
251+
else if (value == 4)
252+
{
253+
// 자동문 - 논블로킹 방식
254+
digitalWrite(11, HIGH);
255+
digitalWrite(8, HIGH);
256+
digitalWrite(5, HIGH);
257+
digitalWrite(12, HIGH);
258+
digitalWrite(9, HIGH);
259+
digitalWrite(6, HIGH);
260+
digitalWrite(13, HIGH);
261+
digitalWrite(10, HIGH);
262+
digitalWrite(7, HIGH);
263+
264+
autoDoorActive = true;
265+
autoDoorPhase = 1;
266+
autoDoorLastTime = millis();
267+
servoMotor.write(0);
268+
}
269+
else
270+
{
271+
servoMotor.write(90);
272+
}
273+
}
274+
else
275+
{
276+
setPortWritable(port);
277+
analogWrite(port, value);
278+
}
279+
remainData = 0;
280+
}
281+
}
282+
283+
// ─────────────────────────────────────────────
284+
// sendPinValues()
285+
// - 디지털/아날로그 값 전송
286+
// ─────────────────────────────────────────────
287+
void sendPinValues()
288+
{
289+
for (int pinNumber = 0; pinNumber < 14; pinNumber++)
290+
{
291+
if (pinNumber == SERVO_MOTOR)
292+
continue;
293+
sendDigitalValue(pinNumber);
294+
}
295+
for (int pinNumber = 0; pinNumber < 6; pinNumber++)
296+
{
297+
sendAnalogValue(pinNumber);
298+
}
299+
}
300+
301+
// ─────────────────────────────────────────────
302+
// sendAnalogValue(), sendDigitalValue()
303+
// - 센서값을 시리얼로 전송
304+
// ─────────────────────────────────────────────
305+
void sendAnalogValue(int pinNumber)
306+
{
307+
int value;
308+
309+
if (pinNumber == 1)
310+
{
311+
// 초음파 센서: A1의 경우 주기를 제한하여 초음파 센서 측정값 사용
312+
if (millis() - lastUltrasonicTime >= 60)
313+
{
314+
lastUltrasonicValue = readUltrasonicSensor();
315+
lastUltrasonicTime = millis();
316+
}
317+
value = lastUltrasonicValue;
318+
}
319+
else if (pinNumber == 2)
320+
{
321+
// 온습도 센서: a2는 DHT11 센서로부터 온도와 습도를 결합한 값을 전송
322+
if (millis() - lastDHTTime >= 2000)
323+
{ // 최소 2초 간격
324+
int temperature = (int)round(dht.readTemperature());
325+
int humidity = (int)round(dht.readHumidity());
326+
if (isnan(temperature) || isnan(humidity))
327+
{
328+
lastDHTValue = 0; // 읽기 실패 시 0 전송
329+
}
330+
else
331+
{
332+
// (온도×100 + 습도)를 5로 나눈 값
333+
lastDHTValue = (uint16_t)round((temperature * 100.0 + humidity) / 5.0);
334+
}
335+
lastDHTTime = millis();
336+
}
337+
value = lastDHTValue;
338+
}
339+
else
340+
{
341+
value = analogRead(pinNumber);
342+
}
343+
344+
// 아날로그 데이터 전송 (2바이트)
345+
Serial.write(0xC0 | ((pinNumber & 0x07) << 3) | ((value >> 7) & 0x07));
346+
Serial.write(value & 0x7F);
347+
}
348+
349+
void sendDigitalValue(int pinNumber)
350+
{
351+
if (isPortWritable(pinNumber))
352+
{
353+
// 출력 모드
354+
Serial.write(0x80 | ((pinNumber & 0x0F) << 2));
355+
}
356+
else
357+
{
358+
// 입력 모드
359+
if (digitalRead(pinNumber) == HIGH)
360+
{
361+
Serial.write(0x80 | ((pinNumber & 0x0F) << 2) | 0x01);
362+
}
363+
else
364+
{
365+
Serial.write(0x80 | ((pinNumber & 0x0F) << 2));
366+
}
367+
}
368+
}
369+
370+
// ─────────────────────────────────────────────
371+
// setPortReadable(), setPortWritable()
372+
// ─────────────────────────────────────────────
373+
void setPortReadable(int port)
374+
{
375+
if (isPortWritable(port))
376+
{
377+
pinMode(port, INPUT);
378+
}
379+
}
380+
381+
void setPortWritable(int port)
382+
{
383+
if (!isPortWritable(port))
384+
{
385+
pinMode(port, OUTPUT);
386+
}
387+
}
388+
389+
boolean isPortWritable(int port)
390+
{
391+
if (port > 7)
392+
{
393+
return bitRead(DDRB, port - 8);
394+
}
395+
else
396+
{
397+
return bitRead(DDRD, port);
398+
}
399+
}

0 commit comments

Comments
 (0)