|
| 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