"01분반 숨통트여조"의 두 판 사이의 차이

env wiki
이동: 둘러보기, 검색
(사용 센서 및 선정 근거)
(완료 작품의 평가)
 
(같은 사용자의 중간 판 24개는 보이지 않습니다)
12번째 줄: 12번째 줄:
  
 
===지도교수===
 
===지도교수===
박승부 교수님
+
서울시립대학교 환경공학부 박승부 교수님
  
 
===개발기간===
 
===개발기간===
143번째 줄: 143번째 줄:
  
 
====포집 속도(V)====
 
====포집 속도(V)====
포집 속도(V)는 한국산업안전보건공단 산업환기설비 지침에서 제시하는 외부식 하방 흡인형 가스상 물질 제거 속도(0.5m/s)를 적용하였다.
+
포집 속도(V)는 한국산업안전보건공단 산업환기설비 지침에서 제시하는 외부식 하방 흡인형 가스상 물질 제거 속도(0.5m/s)를 적용하였다.
  
 
====제어 거리(X)====
 
====제어 거리(X)====
제어 거리(X)는 국내 최대 점유 차량 아반떼의 최저 지상고(0.14m)에 보정계수(1.1, 1.3)를 곱하여 계산하였다.
+
포집 후드는 차량과 머플러 하단부에 설치하여 외부식 하방흡인형으로 설계하였다. 이는 차량의 배기구에서 배출되는 오염가스를 하방에서 즉시 흡입하여 주변으로 확산되기 전에 제거하기 위함이다. 흡기구와 오염원 간 제어거리는 차량의 최저지상고인 0.14m로 설정하였다. 차량은 국내에서 가장 높은 점유율을 보이는 현대 자동차 아반떼를 기준으로 하였다. 아반떼의 경우 배기구에서 바닥으로 내린 수선이 후드 상부에 위치하나, 다른 차량의 경우 배기구 높이나 방향이 달라 이러한 조건을 만족시키지 않을 수 있다. 이에 따라 차종의 구조적 차이와 주차상태의 차이를 고려하여 보정계수를 각각 1.1, 1.3으로 산정하였다. 이후 이 두 계수를 제어거리 0.14m에 곱한 결과 실제 제어거리를 0.2002m로 계산하였다. 또한 흡기구 형태를 한 변의 길이가 0.2m 정사각형으로 가정하였다.
  
 
====흡기구 면적(A)====
 
====흡기구 면적(A)====
후드 제어거리 이론에서 오염원에서 후드까지 거리가 덕트 직경(상당 직경)의 1.5배 이내일 때 유효한 흡입이 가능하다. 제어 거리가 0.2002m이므로 후드(포집 장치) 직경은 0.2002m ÷ 1.5 = 0.13m 이상이어야 한다.  
+
[[파일:후드 유효직경 계산.png]]
따라서 포집 장치 형태를 한 변의 길이가 0.2m인 정사각형으로 가정하여 흡기구 면적(A)을 계산하였다.
+
 
 +
후드 제어거리 이론에 따르면 오염원에서 후드까지 거리가 덕트 직경(상당 직경)의 1.5배 이내일 때 유효한 흡입이 가능하다. 앞서 계산한 제어 거리가 0.2002m이므로 포집 장치의 직경은 0.2002m ÷ 1.5 = 0.13m 이상이어야 한다.  
 +
이에 따라 좀 더 충분한 유효 흡입 효과를 달성하기 위해 0.13보다 큰 0.2m로 산정한 뒤 포집 장치 형태를 정사각형으로 가정하여 흡기구 면적(A)을 계산하였다.
  
 
====포집 배풍량(Q)====
 
====포집 배풍량(Q)====
한국산업안전보건공단 산업환기설비 지침에서 제시하는 배풍량 식을 적용하였다. 포집 속도, 제어 거리, 흡기구 면적을 대입하여 배풍량을 산정하였다.
+
한국산업안전보건공단 산업환기설비 지침에서 제시하는 배풍량 식을 적용하였다. 포집 속도, 제어 거리, 흡기구 면적을 대입하여 배풍량을 산정하였다.
 +
 
 +
배풍량 Q(m^3/min)=V(10X^2+A)
 +
=0.5(10x0.2002x0.2002+0.2X0.2)x60=13.224
  
 
====설계 제원====
 
====설계 제원====
162번째 줄: 167번째 줄:
 
[[파일: 숨통 모식도 1.png]]            [[파일: 숨통 모식도2.png]]
 
[[파일: 숨통 모식도 1.png]]            [[파일: 숨통 모식도2.png]]
  
화살표 부근 수치는 길이를 나타내는 것으로서 단위는 mm이다.
+
좌측 그림은 주차구획, 우칙 그림 포집 장치 배치도로서 Free CAD 프로그램을 활용하여 나타낸 것이다. 화살표 부근 수치는 길이를 나타내며 단위는 mm이다. 주차장법 시행규칙 제3조에서는 평행주차형식 외의 경우 일반형의 주차구획은 너비 2.5m 이상, 길이 5.0m 이상으로 제시하고 있다. 이에 따라 좌측 그림의 주차구획은 2,500mm(2.5m) × 5000mm(5.0m)으로 설계하였다. (주) 하이큐의 2020년 주차블록 설치기준에 따르면 후방 주차라인에서 900mm, 좌우 주차라인에서 50mm 간격을 두어 주차블록을 설치한다. 이 기준을 반영하여 너비 750 mm, 길이 150 mm 주차블록 두 개를 배치하였고, 이를 좌측 그림에 제시하였다.
한 변의 길이가 200mm인 정사각형 포집 장치를 좌우 주차라인에서는 325mm, 후방 주차라인에서는 300mm 거리를 두고자 한다.  
+
포집 장치 배치도는 우측 그림에 제시하였다. 한 변의 길이가 200mm인 정사각형 포집 장치를 주차구획 내에 2개 배치하며, 좌우 주차라인에서는 325mm, 후방 주차라인에서는 300mm 거리를 두고자 한다. 좌우 주차라인에서 325mm 거리를 두는 이유는 포집 장치의 중심을 주차블록 중앙에 맞추기 위함이다. 좌우 주차라인에서 주차블록(너비 750mm)까지의 간격은 50mm이므로 좌우 주차라인에서 주차블록 중심까지의 거리는 50mm + (750mm ÷ 2) = 425mm이다. 이때 포집 장치는 한 변이 200mm이므로, 포집 장치 중심은 그 변으로부터 100mm 떨어진 위치에 있다. 좌우 주차라인에서 포집 장치의 한 변까지의 거리를 325mm로 두면 325mm + 100mm = 425mm이므로, 포집 장치의 중심이 주차블록의 중심과 일치한다.
좌우 주차라인에서 325mm 거리를 두는 이유는 포집 장치의 중심을 주차블록 중앙에 맞추기 위함이다.  
 
후방 주차라인에서 300mm 거리를 두는 이유는 포집 장치 배치 거리에 제시하였다.
 
  
 
====포집 장치 배치 거리====
 
====포집 장치 배치 거리====
 
[[ 파일: 숨통 배치거리.png]]
 
[[ 파일: 숨통 배치거리.png]]
  
주차블록에서 포집 장치까지 배치 거리는 다음과 같이 계산하였다.  
+
주차블록에서 포집 장치까지 배치 거리는 다음과 같이 계산하였다.  
주차블록에서 포집 장치까지 배치 거리 = 1,000mm(차량 뒷바퀴 중심에서 머플러까지 거리) - 320mm(타이어 외경 절반) - 150mm(주차블록 길이) + 70mm(주차블록 상단과 타이어가 겹치는 길이) = 600mm
+
주차블록에서 포집 장치까지 배치 거리 = 1,000mm(차량 뒷바퀴 중심에서 머플러까지 거리) - 320mm(타이어 외경 절반) - 150mm(주차블록 길이) + 70mm(주차블록 상단과 타이어가 겹치는 길이) = 600mm
주차블록 높이 120mm를 고려하면 타이어 반지름 321mm에서 가감된 값은 약 201mm가 된다. 타이어 반지름 321mm를 빗변으로 하는 직각 삼각형에서 피타고라스 정리를 적용하면 밑변은 250.3mm이다.  
+
주차블록 높이 120mm를 고려하면 타이어 반지름 321mm에서 가감된 값은 약 201mm가 된다. 타이어 반지름 321mm를 빗변으로 하는 직각 삼각형에서 피타고라스 정리를 적용하면 밑변은 250.3mm이다.  
타이어 반지름 321mm에서 이 값(250.3mm)을 가감하면 주차블록 상단과 타이어가 겹치는 길이는 약 70mm로 계산된다.  
+
타이어 반지름 321mm에서 이 값(250.3mm)을 가감하면 주차블록 상단과 타이어가 겹치는 길이는 약 70mm로 계산된다.  
따라서 후방 주차라인에서 주차블록까지 거리가 900mm이므로 후방 주차라인에서 포집 장치까지 배치 거리는 900mm - 600mm = 300mm이다.
+
따라서 후방 주차라인에서 주차블록까지 거리가 900mm이므로 후방 주차라인에서 포집 장치까지 배치 거리는 900mm - 600mm = 300mm이다.
  
 
====탐지 항목====
 
====탐지 항목====
192번째 줄: 195번째 줄:
 
[[파일: 숨통 메커니즘.png]]
 
[[파일: 숨통 메커니즘.png]]
  
제어 메커니즘은 차량 입차 및 출차 상황에 따라 상이한 기준을 적용한다. 입차 시에는 HC-SR04 센서로 거리를 감지하여 차량 감지 거리가 25 cm 이하일 경우 포집 장치(FAN)를 가동한다.  
+
제어 메커니즘은 차량 입차 및 출차 상황에 따라 상이한 기준을 적용한다. 입차 시에는 HC-SR04 센서로 거리를 감지하여 차량 감지 거리가 25 cm 이하일 경우 포집 장치(FAN)를 가동한다.  
반면, 출차 시에는 MQ-7 및 AM1002 센서를 활용하여 차량 오염물질 배출에 따른 농도 기준을 적용한다. CO Slope가 6 이상 또는 PM-10 농도가 200 μg/m³이상 또는 TVOC 농도가 1,000 μg/m³이상 중 하나라도 충족할 경우 FAN이 작동한다.  
+
반면, 출차 시에는 MQ-7 및 AM1002 센서를 활용하여 차량 오염물질 배출에 따른 농도 기준을 적용한다. CO Slope가 6 이상 또는 PM-10 농도가 200 μg/m³이상 또는 TVOC 농도가 1,000 μg/m³이상 중 하나라도 충족할 경우 FAN이 작동한다.  
또한 FAN의 최소 작동 시간은 1분으로 설정하였는데, 이는 유타 주립 대학교의 공회전 배출 특성 연구(Martin, 2017)에서 냉간 시동(Cold Start) 직후 0~60초 사이에 CO가 고농도로 배출된다는 보고에 근거하였다.  
+
또한 FAN의 최소 작동 시간은 1분으로 설정하였는데, 이는 유타 주립 대학교의 공회전 배출 특성 연구(Martin, 2017)에서 냉간 시동(Cold Start) 직후 0~60초 사이에 CO가 고농도로 배출된다는 보고에 근거하였다.  
FAN이 1분간 가동된 이후에는 공기질 개선 여부를 판단하고, 농도 기준 이하이면 FAN 작동을 중단한다.
+
FAN이 1분간 가동된 이후에는 공기질 개선 여부를 판단하고, 농도 기준 이하이면 FAN 작동을 중단한다.
  
 
====통합 회로====
 
====통합 회로====
 
[[파일: 숨통 통합회로.png]]
 
[[파일: 숨통 통합회로.png]]
 
   
 
   
통합 회로는 설정된 기준에 따라 FAN이 1분 이상 작동하도록 구현하였다. 또한 Relay Module을 사용하여 가동부 FAN을 센서와 연결하였다.
+
통합 회로는 설정된 기준에 따라 FAN이 1분 이상 작동하도록 구현하였다. 또한 Relay Module을 사용하여 가동부 FAN을 센서와 연결하였다. 전술한 매커니즘을 구현하는 통합 회로 코드는 아래와 같다.
 +
 
 +
#include <Arduino.h>
 +
/***************************************************
 +
  * 1. 핀 및 하드웨어 설정
 +
  ***************************************************/
 +
const uint8_t MQ7_PIN = A0;
 +
// AM1002: TXD->17(RX2), RXD->16(TX2)
 +
HardwareSerial &AM1002_SERIAL  = Serial2;
 +
 +
#define TRIG_PIN 9
 +
#define ECHO_PIN 10
 +
const uint8_t FAN_PIN      = 7;
 +
const uint8_t FAN_ON_LEVEL  = HIGH;
 +
const uint8_t FAN_OFF_LEVEL = LOW;
 +
/***************************************************
 +
  * 2. 변수 및 상수 설정
 +
  ***************************************************/
 +
// 제어 모드 (1: Timer Only, 2: Smart Extension)
 +
int operationMode = 1;
 +
const unsigned long SAMPLE_PERIOD_MS = 1000UL;
 +
unsigned long lastSampleMs = 0;
 +
// CO 관련 (MQ-7)
 +
float CO_raw = 0.0f;
 +
float CO_avg3s = 0.0f;
 +
float prev_CO_avg3s = NAN;
 +
float dCOdt = 0.0f;       
 +
float coBuffer[3];
 +
uint8_t coBufIdx = 0;
 +
uint8_t coBufCnt = 0;
 +
// 임계값 (Thresholds)
 +
const float SLOPE_THR = 6.0f;      // CO 기울기 급증 기준
 +
const float CO_HOLD_THR = 25.0f;    // 유지 모드 해제 기준 (25 ppm)
 +
const int PM10_THR      = 200;      // PM10 트리거 기준 (200 ug/m3)
 +
// TVOC 기준 변경
 +
// 1000 ug/m3 (법적 기준) -> 톨루엔 환산 시 약 265 ppb
 +
const int TVOC_THR      = 265;      // 단위: ppb
 +
// AM1002 데이터
 +
int pm10_val = 0; // ug/m3
 +
int tvoc_val = 0; // ppb
 +
// 차량 감지 (초음파)
 +
const float VEHICLE_THRESHOLD = 23.0f;
 +
float distAvg = NAN;
 +
float distBuffer[3];
 +
uint8_t distBufIdx = 0;
 +
uint8_t distBufCnt = 0;
 +
bool vehicleFlag = false;      // 현재 차량 있음
 +
bool prevVehicleFlag = false;  // 이전 루프 차량 있음
 +
// 팬 제어 상태
 +
const unsigned long FAN_MIN_RUN_TIME_MS = 60000UL; // 최소 1분
 +
bool isFanRunning = false;
 +
unsigned long fanTurnOnTime = 0;
 +
// 1회 작동 확인 플래그
 +
bool hasRunOnce = false;
 +
/***************************************************
 +
  * 3. 유틸리티 함수
 +
  ***************************************************/
 +
void addToBuffer(float *buf, uint8_t &cnt, uint8_t &idx, uint8_t size, float val) {
 +
  buf[idx] = val;
 +
  idx = (idx + 1) % size;
 +
  if (cnt < size) cnt++;
 +
}
 +
float getAvg(float *buf, uint8_t cnt) {
 +
  if (cnt == 0) return NAN;
 +
  float sum = 0;
 +
  for (uint8_t i = 0; i < cnt; i++) sum += buf[i];
 +
  return sum / cnt;
 +
}
 +
float getDistance() {
 +
  digitalWrite(TRIG_PIN, LOW);
 +
  delayMicroseconds(5);
 +
  digitalWrite(TRIG_PIN, HIGH);
 +
  delayMicroseconds(10);
 +
  digitalWrite(TRIG_PIN, LOW);
 +
  long dur = pulseIn(ECHO_PIN, HIGH, 30000);
 +
  if (dur == 0) return NAN;
 +
  return dur * 0.0343f / 2.0f;
 +
}
 +
/***************************************************
 +
  * 4. 센서 읽기 함수
 +
  ***************************************************/
 +
void readMQ7() {
 +
  int rawADC = analogRead(MQ7_PIN);
 +
  float pseudoPPM = map(rawADC, 0, 1023, 0, 500);
 +
  addToBuffer(coBuffer, coBufCnt, coBufIdx, 3, pseudoPPM);
 +
  CO_raw = pseudoPPM;
 +
}
 +
void readAM1002() {
 +
  byte reqCmd[] = {0x11, 0x01, 0x16, 0xD8};
 +
  AM1002_SERIAL.write(reqCmd, 4);
 +
  delay(50);
 +
  if (AM1002_SERIAL.available() >= 22) {
 +
    if (AM1002_SERIAL.peek() != 0x16) {
 +
      AM1002_SERIAL.read();
 +
      return;
 +
    }
 +
    byte buffer[25];
 +
    AM1002_SERIAL.readBytes(buffer, 22);
 +
    if (buffer[0] == 0x16 && buffer[2] == 0x16) {
 +
      tvoc_val = buffer[3] * 256 + buffer[4];  // ppb 단위
 +
      pm10_val = buffer[11] * 256 + buffer[12]; // ug/m3 단위
 +
    }
 +
  }
 +
}
 +
/***************************************************
 +
  * 5. Setup
 +
  ***************************************************/
 +
void setup() {
 +
  Serial.begin(115200);
 +
  delay(1000);
 +
  AM1002_SERIAL.begin(9600);
 +
  pinMode(MQ7_PIN, INPUT);
 +
  pinMode(FAN_PIN, OUTPUT);
 +
  digitalWrite(FAN_PIN, FAN_OFF_LEVEL); 
 +
  pinMode(TRIG_PIN, OUTPUT);
 +
  pinMode(ECHO_PIN, INPUT);
 +
  Serial.println(F("=== [System Booting] Ver 6.3 (TVOC Display Update) ==="));
 +
  Serial.println(F("Calibrating Sensors... Please wait."));
 +
  // 초기 주차 상태 인식
 +
  float initDistSum = 0;
 +
  int validReadings = 0;
 +
  for(int i=0; i<10; i++) {
 +
    float d = getDistance();
 +
    if(!isnan(d)) {
 +
      initDistSum += d;
 +
      validReadings++;
 +
    }
 +
    delay(50);
 +
  }
 +
  if(validReadings > 0) {
 +
    float initAvg = initDistSum / validReadings;
 +
    Serial.print(F("Initial Distance: "));
 +
    Serial.print(initAvg); Serial.println(F(" cm"));
 +
   
 +
    if(initAvg <= VEHICLE_THRESHOLD) {
 +
      prevVehicleFlag = true;
 +
      Serial.println(F(">> Note: Vehicle Detected at Startup. Entry Trigger DISABLED."));
 +
    } else {
 +
      prevVehicleFlag = false;
 +
      Serial.println(F(">> Note: No Vehicle at Startup."));
 +
    }
 +
  } else {
 +
    Serial.println(F(">> Warning: Ultrasonic Sensor Error!"));
 +
  }
 +
  Serial.println(F("=== System Ready ==="));
 +
  Serial.println(F("Default: MODE 1 (Timer Only)"));
 +
}
 +
/***************************************************
 +
  * 6. Loop
 +
  ***************************************************/
 +
void loop() {
 +
  // 모드 전환 명령어
 +
  if (Serial.available() > 0) {
 +
    String input = Serial.readStringUntil('\n');
 +
    input.trim();
 +
    if (input.equalsIgnoreCase("MODE1")) {
 +
      operationMode = 1;
 +
      Serial.println(F(">> [SYSTEM] Switched to MODE 1 (Simple Timer)"));
 +
    } else if (input.equalsIgnoreCase("MODE2")) {
 +
      operationMode = 2;
 +
      Serial.println(F(">> [SYSTEM] Switched to MODE 2 (Smart Extension)"));
 +
    }
 +
  }
 +
  unsigned long now = millis();
 +
  if (now - lastSampleMs >= SAMPLE_PERIOD_MS) {
 +
    float dt = (now - lastSampleMs) / 1000.0f;
 +
    lastSampleMs = now;
 +
    // --- 1) 센서 데이터 수집 ---
 +
    readMQ7();
 +
    CO_avg3s = getAvg(coBuffer, coBufCnt);
 +
    readAM1002();
 +
    float dist = getDistance();
 +
    if (!isnan(dist)) addToBuffer(distBuffer, distBufCnt, distBufIdx, 3, dist);
 +
    distAvg = getAvg(distBuffer, distBufCnt);
 +
    // --- 2) 데이터 가공 ---
 +
    if (!isnan(prev_CO_avg3s)) {
 +
      dCOdt = (CO_avg3s - prev_CO_avg3s) / dt;
 +
    }
 +
    prev_CO_avg3s = CO_avg3s;   
 +
    vehicleFlag = (!isnan(distAvg) && distAvg <= VEHICLE_THRESHOLD);
 +
    // --- 3) 제어 로직 ---
 +
    if (hasRunOnce) {
 +
        // 잠금 상태 (작동 안 함)
 +
    }
 +
    else {
 +
        // [트리거 조건 확인]
 +
        bool vehicleEntry = (vehicleFlag && !prevVehicleFlag);
 +
        bool isHighSlope = (!isnan(dCOdt) && dCOdt > SLOPE_THR);
 +
        bool isHighPM    = (pm10_val >= PM10_THR);   
 +
        // [수정] TVOC 트리거는 ppb 기준(265)으로 판단
 +
        bool isHighTVOC  = (tvoc_val >= TVOC_THR);     
 +
        bool pollutionTrigger = (vehicleFlag && (isHighSlope || isHighPM || isHighTVOC));
 +
        // 팬 켜기
 +
        if (!isFanRunning) {
 +
          if (vehicleEntry || pollutionTrigger) {
 +
            digitalWrite(FAN_PIN, FAN_ON_LEVEL);
 +
            isFanRunning = true;
 +
            fanTurnOnTime = now;
 +
            Serial.println(F(">> [EVENT] FAN ON"));
 +
           
 +
            if(vehicleEntry) Serial.println(F("  Reason: Vehicle Entry"));
 +
            if(pollutionTrigger) {
 +
              Serial.print(F("  Reason: Pollution Detected ("));
 +
              if(isHighSlope) Serial.print(F("Slope "));
 +
              if(isHighPM)    Serial.print(F("PM "));
 +
              if(isHighTVOC)  Serial.print(F("TVOC "));
 +
              Serial.println(F(")"));
 +
            }
 +
          }
 +
        }   
 +
        // 팬 끄기 판단
 +
        if (isFanRunning) {
 +
          bool timeExpired = (now - fanTurnOnTime > FAN_MIN_RUN_TIME_MS);
 +
          bool shouldStop = false;
 +
         
 +
          if (operationMode == 1) {
 +
            if (timeExpired) shouldStop = true;
 +
          }
 +
          else if (operationMode == 2) {
 +
            // [수정] MODE 2 유지 조건도 ppb 기준으로 비교
 +
            bool airClean = (CO_raw < CO_HOLD_THR) && (pm10_val < PM10_THR) && (tvoc_val < TVOC_THR);
 +
            if (timeExpired) {
 +
              if (airClean) shouldStop = true;
 +
              else {
 +
                if ((now / 1000) % 5 == 0) Serial.println(F(">> [HOLD] Fan Extending... (Pollution High)"));
 +
              }
 +
            }
 +
          }
 +
          if (shouldStop) {
 +
            digitalWrite(FAN_PIN, FAN_OFF_LEVEL);
 +
            isFanRunning = false;
 +
            hasRunOnce = true; // 잠금 활성화
 +
            Serial.println(F(">> [SYSTEM STOP] Fan Stopped. System LOCKED."));
 +
            Serial.println(F(">> Press RESET button to restart."));
 +
          }
 +
        }
 +
    }
 +
    prevVehicleFlag = vehicleFlag;
 +
    // --- 4) 상태 모니터링 출력 ---
 +
    Serial.print(F("[MONITOR] ")); 
 +
    if (hasRunOnce) Serial.print(F("LOCKED"));
 +
    else Serial.print(operationMode == 1 ? "M1" : "M2");
 +
    Serial.print(F(" | CO:"));    Serial.print(CO_raw, 0);
 +
    Serial.print(F(" | Slope:")); Serial.print(dCOdt, 2);
 +
    Serial.print(F(" | PM:"));    Serial.print(pm10_val);
 +
    //  시리얼 모니터 출력 시 ug/m3 단위로 환산하여 표시
 +
    // 1 ppb * (92.14 / 24.45) = 3.7685 ug/m3
 +
    float tvoc_ugm3 = tvoc_val * 3.7685f;
 +
    Serial.print(F(" | TVOC(ug):")); Serial.print(tvoc_ugm3, 0); //
 +
    Serial.print(F(" | Dist:"));
 +
    if(isnan(distAvg)) Serial.print(F("Err"));
 +
    else Serial.print(distAvg, 1);
 +
    Serial.print(F("cm | Vehicle:"));
 +
    Serial.print(vehicleFlag ? "YES" : "NO");
 +
    Serial.print(F(" | FAN:"));
 +
    Serial.println(isFanRunning ? "ON" : "OFF");
 +
  }
 +
}
  
 
==결과 및 평가==
 
==결과 및 평가==
 
===완료 작품의 소개===
 
===완료 작품의 소개===
 
====프로토타입 사진====
 
====프로토타입 사진====
 +
[[파일:시제품 모형.jpg|800픽셀]]
  
 +
위 모형은 지하주차장의 주차구획 한 칸을 모사한 것이며, 표지된 MQ-7(CO), HC-SR04(초음파), AM1002(PM-10, VOC) 센서들로 통합 회로를 구축하였다. 또한, 차량 혹은 농도 감지기준 중 한 가지 이상 충족 시 주차블록 후단에 위치한 환기팬이 가동되도록 설계하였다. 팬이 흡기한 공기는 바닥 하부 덕트를 통해 모형 밖으로 배출된다.
  
 
====실험 결과====
 
====실험 결과====
 +
[[파일:팬온.jpg|450픽셀|왼쪽]]
 +
[[파일:팬오프.jpg|450픽셀|가운데|]]
  
 +
 +
FAN 작동 유무에 따라 오염물질의 농도가 변하였으며, 그중 1분간 FAN ON의 경우 CO 제어가 가능함을 확인할 수 있다. CO 농도는 최대 기준 29초 후 50% 이상이 저감되었고 1분 후에는 약 65%가 저감되었다. PM-10, VOC 또한 FAN 작동 시 농도가 감소함을 확인하였다.
  
 
===완료 작품의 평가===
 
===완료 작품의 평가===
 
+
[[파일:숨통결과.jpg|700픽셀]]
  
 
===포스터===
 
===포스터===
 
+
[[파일:숨통트여조_포스터.png]]
  
 
===개발 사업비 내역서===
 
===개발 사업비 내역서===
 +
[[파일:숨통트여조_개발사업비 내역서.jpg]]
  
 
==향후 계획==
 
==향후 계획==

2025년 12월 22일 (월) 09:50 기준 최신판

프로젝트 개요

기술개발 과제

국문 : 근접 포집을 활용한 지하주차장 공기질 개선 시스템 구축

영문 : Air Quality Improvement System in Basement Parking Lot Using Source Capture

과제 팀명

숨통트여조

지도교수

서울시립대학교 환경공학부 박승부 교수님

개발기간

2025년 9월 ~ 2025년 12월 (총 4개월)

구성원 소개

서울시립대학교 환경공학부 20208900** 김*홍(조장)

서울시립대학교 환경공학부 20208900** 김*혁

서울시립대학교 환경공학부 20208900** 백*수

서울시립대학교 환경공학부 20208900** 정*호


서론

개발 과제의 개요

개발 과제 요약

본 과제는 지하주차장 내 근접 포집 장치를 도입하여 실내공기질을 개선하는 것을 목표로 한다. 후면 주차를 가정한 상황에서 차량이 주차구획에 진입할 때, 포집 장치가 배기가스 오염물질(PM-10, CO, VOCs)을 실시간으로 포집하여 농도를 제어한다. CO 센서로 농도를 감지하고 초음파 센서로 차량을 감지하여 실내공기질을 개선하고자 한다.

개발 과제의 배경

  • 실내공간으로서 지하주차장의 공기질 문제

지하주차장은 자동차가 한정된 밀폐공간에서 움직이면서 발생하는 배기가스로 인해 주요 실내공기 오염 공간이 되고 있다. 2004년 5월 국민의 건강증진과 더 쾌적한 실내 공기질을 확보하기 위해 다중이용시설 등의 실내공기질 관리법이 제정, 시행되고 있으며, 해당 법안에서 관리하는 일정 규모 이상의 다중이용시설에는 지하주차장도 포함된다. 2016년 12월 22일 다중이용시설 실내공기질 오염 기준 개정안에 따라 실내 공기질 유지를 위한 오염물질로는 미세먼지(PM10), 초미세먼지(PM2.5), 폼알데하이드, 일산화탄소, 이산화질소 등이 있다.

  • 지하주차장 공기오염과 건강 피해

지하주차장의 오염된 실내 공기에 장시간 노출될 경우 사망으로 이어질 수도 있는데, 7년 4개월간 지하주차장에서 일한 근로자가 폐암으로 사망한 사건이 대표적이다. 이에 대해 법원은 장기간 환기 상태가 불량한 지하주차장에서 근무하며 발암물질에 노출된 점과 폐암 발병 사이의 인과성이 충분하다고 인정하였다(2011구합8642).

개발 과제의 목표 및 내용

전체급기, 전체배기에 의한 희석 환기를 골자로 하는 기존 환기 방식은 환기량 조절과 환기의 연속성 확보 측면에서 장점이 있으나, 공간 내부의 오염물질 평균 농도를 낮추는 데 긴 시간이 소요된다는 한계가 있다. 이를 극복하기 위해, 오염물질을 오염원 근처에서 확산 전에 포집하는 부분적 국소 포집 시스템을 설계해 환기 효율을 높이는 것을 목표로 한다. 구체적인 설계 목표는 다음과 같다.

1. 제거 대상 오염물질 선정

2. 포집 장치 설계 배풍량 산정

3. 자동제어를 위한 작동 기준 선정

4. 배출원 근접 포집 시스템 설계

관련 기술의 현황

관련 기술의 현황 및 분석(State of art)

(1) 지하주차장 환기 팬

숨통 관련기술.png

(2) CO 감지 센서 및 초음파 센서

CO 감지 센서 및 초음파 센서.png

SWOT 분석

숨통 SWOT 분석.png

경제성 분석

비용

비용 분석표.png


총 비용은 일회성인 초기비용과 매년 부과되는 유지관리비용으로 구분하였다. 초기비용에는 부품비와 시공비가 포함된다. 면적 기준 시공비는 경기융합타운 환승주차장 신축공사 도급내역을 참고하여 선정하였으며, 근접 포집 장치의 가동률은 차량 1대당 2분 가동되고 주차 회전율은 인천연구원의 인천광역시 유료 공공주차장의 일평균 주차 회전율 2.56을 상회하는 4.0이라고 가정하였다. 추가로, 시제품에는 HC-SR04 초음파 센서(1,100원)를 사용하였으나, 부품비에는 실제 적용할 DO-UUSS500 센서를 기준으로 반영하였다.

편익

편익 분석표.png


편익은 이노텍 TIV-250D 팬 사용을 가정한 기존 환기 방식의 연간 소모 전력량과 본 과제의 근접 포집 장치의 연간 소모 전력량을 비교하여 계산하였다.

경제성 분석 결과

Bcratio 분석표.png


비용, 편익 분석을 근거로 8년차까지의 B/C ratio를 계산하였다. 본 시스템은 10년 이상의 장기간 사용을 전제하는 장치로, 시공 후 8년차에 B/C ratio가 1 이상을 보이며 장기간 유지할수록 경제적 기대효과는 더욱 증대될 것으로 기대된다.

개발과제의 기대효과

기술적 기대효과

1) 오염물질이 공간 전체로 확산하기 이전, 배출원 인근에서 직접 포집하는 구조를 통해 동일한 제거 효과를 얻기 위해 요구되는 배풍량 및 시간을 줄일 수 있다.

2) 오염 농도의 단시간 피크에 즉각 대응할 수 있다.

3) 초음파 센서와 농도 센서를 연동한 자동 제어로 오염도가 높은 구역만 선택적으로 가동할 수 있어 불필요한 운전을 줄이고 전력을 절감할 수 있다.

사회적 기대효과

1) 시민의 오염물질 노출을 근원적으로 차단하여 호흡기 질환 예방에 기여할 수 있다.

2) 사후가 아닌 사전에 고농도 노출 상황을 방지하는 방향의 설계로 기존 환기 방식의 한계를 보완하고 오염물질 집중 배출 기간에 효과적으로 대응하여 지하주차장 환경을 개선할 수 있다.

기술개발 일정 및 추진체계

숨통 개발일정.png

설계

설계사양

제품의 요구사항

제품 요구사항.png

QFD

숨통트여조 QFD.png

QFD(Quality Function Deployment) 분석에서 고객 요구 품질과 기술 특성을 매핑한 결과, '포집 성능'이 최우선 과제로 도출되었다.

개념설계안

설계 흐름도

개념설계안 숨통트여조.png

개념 설계에 앞서 효율적인 설계를 위해 지하 주차장 환기 장치에 필요한 체계를 차량 감지, 오염물질 포집, 자동 제어, 환기 효율 총 4가지로 정리하였다. 이후 이렇게 선정된 4가지 체계를 설계하는데 있어서 필요한 요구사항을 상단 표와 같이 산정하였고 이에 따라 지하주차장 하부 국소 포집장치를 실현하는데 사용할 구체적인 목표를 설정하였다

포집 장치 배치

숨통 머플러.png

자동차의 배기가스는 엔진의 압축과 폭발, 배기 과정에서 생성되며, 자동차 후면에 설치된 머플러를 통해 방출되게 된다. 이때 머플러의 위치는 자동차 종류마다 상이한데 기아 자동차 K5의 머플러는 차량의 우측 하단이나 양쪽 하단에 위치하며 현대 자동차 벨로스터의 머플러는 중앙에 위치한다. 이처럼 지하주차장의 특정 구획에 한개만 포집 장치를 설치하는 것은 머플러 위치의 변동에 효과적으로 대응하기 어려울 수 있다. 따라서 후면 주차를 가정한 상황에서 배기가스의 포집 장치는 주차라인과 주차블록 사이 양쪽 하단에 배치하도록 한다.

CO 배출 매커니즘 탐구 및 작동 시간 산출

MQ7 작동 매커니즘.png

자동차에서의 HC 및 CO 발생 매커니즘은 상단 그림과 같다. 지하주차장에서 자동차는 일반 주행, 주차를 하는 단계인 후면 주행, 그리고 주차했던 자동차에 시동을 거는 단계로서 주행한다. 그 중 주차한 자동차에 시동을 거는 단계에서 타 단계에 비해 더 많은 양의 일산화탄소를 비롯한 오염물질을 배출하게 된다. 그 이유는 자동차에 설치된 촉매 변환기가 불완전 연소에서 발생하는 일산화탄소와 탄화수소, 질소산화물을 변환시키는데 필요한 온도에 달성하는데 시간이 소모되기 때문이다. 특히 여름과 겨울의 온도차가 큰 환경의 경우 계절에 따라 냉간 시동으로 발생하는 오염물질 양의 변화의 폭이 크다. 2013년 캐나다 환경부의 조사 결과 22도에서 영하 18도로 변할 경우 CO를 비롯한 일부 배출가스가 10배 가량 늘어난다는 사실을 발견했다.

유타주 CO 수정.png

2017년 5월 유타 주립 대학교에서는 자동차의 시동 단계를 냉간 시동, 열간 시동, 공회전으로 나누어 CO의 방출 패턴과 그 양에 대해서 보고서를 작성하였다. 이에 따르면 상단 그림과 같이 냉간 시동시 열간 시동에 비해 89%에서 98% 더 많은 CO가 배출되었으며 자동차의 세대에 따라 43초에서 179초까지 다양하게 나타났다. 따라서 환기 장치를 냉간 시동시 일산화탄소가 집중적으로 방출되는 1분~2분 내외에 일산화탄소의 80%를 넘는 농도 변화를 감지하여 작동하게 하는 것이 적절할 것이다.

이론적 계산

포집 속도(V)

포집 속도(V)는 한국산업안전보건공단 산업환기설비 지침에서 제시하는 외부식 하방 흡인형 가스상 물질 제거 속도(0.5m/s)를 적용하였다.

제어 거리(X)

포집 후드는 차량과 머플러 하단부에 설치하여 외부식 하방흡인형으로 설계하였다. 이는 차량의 배기구에서 배출되는 오염가스를 하방에서 즉시 흡입하여 주변으로 확산되기 전에 제거하기 위함이다. 흡기구와 오염원 간 제어거리는 차량의 최저지상고인 0.14m로 설정하였다. 차량은 국내에서 가장 높은 점유율을 보이는 현대 자동차 아반떼를 기준으로 하였다. 아반떼의 경우 배기구에서 바닥으로 내린 수선이 후드 상부에 위치하나, 다른 차량의 경우 배기구 높이나 방향이 달라 이러한 조건을 만족시키지 않을 수 있다. 이에 따라 차종의 구조적 차이와 주차상태의 차이를 고려하여 보정계수를 각각 1.1, 1.3으로 산정하였다. 이후 이 두 계수를 제어거리 0.14m에 곱한 결과 실제 제어거리를 0.2002m로 계산하였다. 또한 흡기구 형태를 한 변의 길이가 0.2m 정사각형으로 가정하였다.

흡기구 면적(A)

후드 유효직경 계산.png

후드 제어거리 이론에 따르면 오염원에서 후드까지 거리가 덕트 직경(상당 직경)의 1.5배 이내일 때 유효한 흡입이 가능하다. 앞서 계산한 제어 거리가 0.2002m이므로 포집 장치의 직경은 0.2002m ÷ 1.5 = 0.13m 이상이어야 한다. 이에 따라 좀 더 충분한 유효 흡입 효과를 달성하기 위해 0.13보다 큰 0.2m로 산정한 뒤 포집 장치 형태를 정사각형으로 가정하여 흡기구 면적(A)을 계산하였다.

포집 배풍량(Q)

한국산업안전보건공단 산업환기설비 지침에서 제시하는 배풍량 식을 적용하였다. 포집 속도, 제어 거리, 흡기구 면적을 대입하여 배풍량을 산정하였다.

배풍량 Q(m^3/min)=V(10X^2+A)
=0.5(10x0.2002x0.2002+0.2X0.2)x60=13.224

설계 제원

숨통 설계제원.png

상세설계 내용

모식도

숨통 모식도 1.png 숨통 모식도2.png

좌측 그림은 주차구획, 우칙 그림 포집 장치 배치도로서 Free CAD 프로그램을 활용하여 나타낸 것이다. 화살표 부근 수치는 길이를 나타내며 단위는 mm이다. 주차장법 시행규칙 제3조에서는 평행주차형식 외의 경우 일반형의 주차구획은 너비 2.5m 이상, 길이 5.0m 이상으로 제시하고 있다. 이에 따라 좌측 그림의 주차구획은 2,500mm(2.5m) × 5000mm(5.0m)으로 설계하였다. (주) 하이큐의 2020년 주차블록 설치기준에 따르면 후방 주차라인에서 900mm, 좌우 주차라인에서 50mm 간격을 두어 주차블록을 설치한다. 이 기준을 반영하여 너비 750 mm, 길이 150 mm 주차블록 두 개를 배치하였고, 이를 좌측 그림에 제시하였다. 포집 장치 배치도는 우측 그림에 제시하였다. 한 변의 길이가 200mm인 정사각형 포집 장치를 주차구획 내에 2개 배치하며, 좌우 주차라인에서는 325mm, 후방 주차라인에서는 300mm 거리를 두고자 한다. 좌우 주차라인에서 325mm 거리를 두는 이유는 포집 장치의 중심을 주차블록 중앙에 맞추기 위함이다. 좌우 주차라인에서 주차블록(너비 750mm)까지의 간격은 50mm이므로 좌우 주차라인에서 주차블록 중심까지의 거리는 50mm + (750mm ÷ 2) = 425mm이다. 이때 포집 장치는 한 변이 200mm이므로, 포집 장치 중심은 그 변으로부터 100mm 떨어진 위치에 있다. 좌우 주차라인에서 포집 장치의 한 변까지의 거리를 325mm로 두면 325mm + 100mm = 425mm이므로, 포집 장치의 중심이 주차블록의 중심과 일치한다.

포집 장치 배치 거리

숨통 배치거리.png

주차블록에서 포집 장치까지 배치 거리는 다음과 같이 계산하였다. 주차블록에서 포집 장치까지 배치 거리 = 1,000mm(차량 뒷바퀴 중심에서 머플러까지 거리) - 320mm(타이어 외경 절반) - 150mm(주차블록 길이) + 70mm(주차블록 상단과 타이어가 겹치는 길이) = 600mm 주차블록 높이 120mm를 고려하면 타이어 반지름 321mm에서 가감된 값은 약 201mm가 된다. 타이어 반지름 321mm를 빗변으로 하는 직각 삼각형에서 피타고라스 정리를 적용하면 밑변은 250.3mm이다. 타이어 반지름 321mm에서 이 값(250.3mm)을 가감하면 주차블록 상단과 타이어가 겹치는 길이는 약 70mm로 계산된다. 따라서 후방 주차라인에서 주차블록까지 거리가 900mm이므로 후방 주차라인에서 포집 장치까지 배치 거리는 900mm - 600mm = 300mm이다.

탐지 항목

CO, PM-10, TVOC는 지하 주차장에서 차량 운행으로 발생하는 주 오염물질인 동시에 실내질 공기 기준에 관리 및 권고 항목으로 포함된 물질이다. 따라서 본 설계에서는 세 가지 오염물질을 탐지하여 작동 기준을 설정하였다. 냉간 시동으로 가장 많이 배출되며 관리 기준에 속한 CO는 핵심 제어 대상 오염물질이며, PM-10과 TVOC는 CO의 농도 변화를 보조하는 공기질 평가 지표로 선정되었다. 한편 차량의 입차시 방출되는 차량의 오염물질에도 대응하기 위해 차량의 유무 역시 감지 지표로 설정하였다.

사용 센서 및 선정 근거

숨통 센서.png

본 회로에서는 오염물질과 차량을 탐지하기 위해 MQ-7, AM1002, HC-SR04 센서를 사용하였다. MQ-7 센서는 주석 산화물로 이루어진 반도체 박막의 표면 흡착 산소와 CO가 반응하며 전도도의 증가를 감지하는 방식으로 작동한다. 이는 –20℃~50℃의 범위에서 사용할 수 있어 주차장 내부 환경에서 사용이 적합하며 20–2000 ppm의 폭 넓은 일산화탄소 농도를 검출 할 수 있어 다중이용시설 등의 실내공기질관리법에 따라 규정되어 있는 일산화탄소 기준을 넘는지 여부를 확인하기에 적합하다. 이에 따라 본 설계에서 MQ-7 센서는 시간 변화에 따른 CO 농도 기울기(CO Slope)를 출력하고 해당 농도의 기울기가 급격하게 증가할 경우 차량의 냉간 시동이 이루어지고 있다는 판단을 내리도록 할 것이다.. AM1002 센서는 PM-10 및 TVOC 농도와 온습도를 측정할 수 있는 아두이노 센서로 앞선 항목에서 CO의 보조적 감지 수단이자 공기질 평가 기준치를 제공해주는 역할을 하게 된다. HC-SR04 센서는 최대 300cm까지의 거리를 측정할 수 있는 아두이노 초음파 센서이다. HC-SRO4센서는 천장에서 바닥까지 거리를 측정해 사전에 측정된 차량의 없을 경우의 거리를 기준으로 천장에서 바닥까지의 거리가 특정 값 이하로 떨어지게 될 경우 현재 해당 위치에 차량이 입차하였기 때문에 변화가 생겼다고 판단하여 차량의 입차 알림을 보내는 역할을 하게 될 것이다.

따라서 작동 기준으로서 구체적인 농도 기준(CO Slope, PM-10, TVOC) 및 차량 감지 거리 기준을 설정하여야 하며 이는 후술할 제어 메커니즘에 제시하였다.

제어 메커니즘

숨통 메커니즘.png

제어 메커니즘은 차량 입차 및 출차 상황에 따라 상이한 기준을 적용한다. 입차 시에는 HC-SR04 센서로 거리를 감지하여 차량 감지 거리가 25 cm 이하일 경우 포집 장치(FAN)를 가동한다. 반면, 출차 시에는 MQ-7 및 AM1002 센서를 활용하여 차량 오염물질 배출에 따른 농도 기준을 적용한다. CO Slope가 6 이상 또는 PM-10 농도가 200 μg/m³이상 또는 TVOC 농도가 1,000 μg/m³이상 중 하나라도 충족할 경우 FAN이 작동한다. 또한 FAN의 최소 작동 시간은 1분으로 설정하였는데, 이는 유타 주립 대학교의 공회전 배출 특성 연구(Martin, 2017)에서 냉간 시동(Cold Start) 직후 0~60초 사이에 CO가 고농도로 배출된다는 보고에 근거하였다. FAN이 1분간 가동된 이후에는 공기질 개선 여부를 판단하고, 농도 기준 이하이면 FAN 작동을 중단한다.

통합 회로

숨통 통합회로.png

통합 회로는 설정된 기준에 따라 FAN이 1분 이상 작동하도록 구현하였다. 또한 Relay Module을 사용하여 가동부 FAN을 센서와 연결하였다. 전술한 매커니즘을 구현하는 통합 회로 코드는 아래와 같다.

#include <Arduino.h>
/***************************************************
 * 1. 핀 및 하드웨어 설정
 ***************************************************/
const uint8_t MQ7_PIN = A0;
// AM1002: TXD->17(RX2), RXD->16(TX2)
HardwareSerial &AM1002_SERIAL  = Serial2;

#define TRIG_PIN 9
#define ECHO_PIN 10
const uint8_t FAN_PIN       = 7;
const uint8_t FAN_ON_LEVEL  = HIGH; 
const uint8_t FAN_OFF_LEVEL = LOW;
/***************************************************
 * 2. 변수 및 상수 설정
 ***************************************************/
// 제어 모드 (1: Timer Only, 2: Smart Extension)
int operationMode = 1;
const unsigned long SAMPLE_PERIOD_MS = 1000UL;
unsigned long lastSampleMs = 0;
// CO 관련 (MQ-7)
float CO_raw = 0.0f;
float CO_avg3s = 0.0f;
float prev_CO_avg3s = NAN;
float dCOdt = 0.0f;         
float coBuffer[3];
uint8_t coBufIdx = 0;
uint8_t coBufCnt = 0;
// 임계값 (Thresholds)
const float SLOPE_THR = 6.0f;       // CO 기울기 급증 기준
const float CO_HOLD_THR = 25.0f;    // 유지 모드 해제 기준 (25 ppm)
const int PM10_THR      = 200;      // PM10 트리거 기준 (200 ug/m3)
// TVOC 기준 변경
// 1000 ug/m3 (법적 기준) -> 톨루엔 환산 시 약 265 ppb
const int TVOC_THR      = 265;      // 단위: ppb
// AM1002 데이터
int pm10_val = 0; // ug/m3
int tvoc_val = 0; // ppb
// 차량 감지 (초음파)
const float VEHICLE_THRESHOLD = 23.0f; 
float distAvg = NAN;
float distBuffer[3];
uint8_t distBufIdx = 0;
uint8_t distBufCnt = 0;
bool vehicleFlag = false;      // 현재 차량 있음
bool prevVehicleFlag = false;  // 이전 루프 차량 있음
// 팬 제어 상태
const unsigned long FAN_MIN_RUN_TIME_MS = 60000UL; // 최소 1분
bool isFanRunning = false;
unsigned long fanTurnOnTime = 0;
// 1회 작동 확인 플래그
bool hasRunOnce = false;
/***************************************************
 * 3. 유틸리티 함수
 ***************************************************/
void addToBuffer(float *buf, uint8_t &cnt, uint8_t &idx, uint8_t size, float val) {
  buf[idx] = val;
  idx = (idx + 1) % size;
  if (cnt < size) cnt++;
}
float getAvg(float *buf, uint8_t cnt) {
  if (cnt == 0) return NAN;
  float sum = 0;
  for (uint8_t i = 0; i < cnt; i++) sum += buf[i];
  return sum / cnt;
}
float getDistance() {
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(5);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);
  long dur = pulseIn(ECHO_PIN, HIGH, 30000);
  if (dur == 0) return NAN;
  return dur * 0.0343f / 2.0f;
}
/***************************************************
 * 4. 센서 읽기 함수
 ***************************************************/
void readMQ7() {
  int rawADC = analogRead(MQ7_PIN);
  float pseudoPPM = map(rawADC, 0, 1023, 0, 500);
  addToBuffer(coBuffer, coBufCnt, coBufIdx, 3, pseudoPPM);
  CO_raw = pseudoPPM;
}
void readAM1002() {
  byte reqCmd[] = {0x11, 0x01, 0x16, 0xD8};
  AM1002_SERIAL.write(reqCmd, 4);
  delay(50);
  if (AM1002_SERIAL.available() >= 22) {
    if (AM1002_SERIAL.peek() != 0x16) {
      AM1002_SERIAL.read(); 
      return;
    }
    byte buffer[25]; 
    AM1002_SERIAL.readBytes(buffer, 22);
    if (buffer[0] == 0x16 && buffer[2] == 0x16) {
      tvoc_val = buffer[3] * 256 + buffer[4];   // ppb 단위
      pm10_val = buffer[11] * 256 + buffer[12]; // ug/m3 단위
    }
  }
} 
/***************************************************
 * 5. Setup
 ***************************************************/
void setup() {
  Serial.begin(115200);
  delay(1000); 
  AM1002_SERIAL.begin(9600);
  pinMode(MQ7_PIN, INPUT); 
  pinMode(FAN_PIN, OUTPUT);
  digitalWrite(FAN_PIN, FAN_OFF_LEVEL);   
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  Serial.println(F("=== [System Booting] Ver 6.3 (TVOC Display Update) ==="));
  Serial.println(F("Calibrating Sensors... Please wait."));
  // 초기 주차 상태 인식
  float initDistSum = 0;
  int validReadings = 0;
  for(int i=0; i<10; i++) {
    float d = getDistance();
    if(!isnan(d)) {
      initDistSum += d;
      validReadings++;
    }
    delay(50);
  }
 if(validReadings > 0) {
   float initAvg = initDistSum / validReadings;
   Serial.print(F("Initial Distance: "));
   Serial.print(initAvg); Serial.println(F(" cm"));
   
   if(initAvg <= VEHICLE_THRESHOLD) {
     prevVehicleFlag = true;
     Serial.println(F(">> Note: Vehicle Detected at Startup. Entry Trigger DISABLED."));
   } else {
     prevVehicleFlag = false;
     Serial.println(F(">> Note: No Vehicle at Startup."));
   }
 } else {
   Serial.println(F(">> Warning: Ultrasonic Sensor Error!"));
 } 
  Serial.println(F("=== System Ready ==="));
  Serial.println(F("Default: MODE 1 (Timer Only)"));
}
/***************************************************
 * 6. Loop
 ***************************************************/
void loop() {
  // 모드 전환 명령어
  if (Serial.available() > 0) {
    String input = Serial.readStringUntil('\n');
    input.trim();
    if (input.equalsIgnoreCase("MODE1")) {
      operationMode = 1;
      Serial.println(F(">> [SYSTEM] Switched to MODE 1 (Simple Timer)"));
    } else if (input.equalsIgnoreCase("MODE2")) {
      operationMode = 2;
      Serial.println(F(">> [SYSTEM] Switched to MODE 2 (Smart Extension)"));
    }
  }
  unsigned long now = millis();
  if (now - lastSampleMs >= SAMPLE_PERIOD_MS) {
    float dt = (now - lastSampleMs) / 1000.0f;
    lastSampleMs = now;
    // --- 1) 센서 데이터 수집 ---
    readMQ7(); 
    CO_avg3s = getAvg(coBuffer, coBufCnt); 
    readAM1002();
    float dist = getDistance();
    if (!isnan(dist)) addToBuffer(distBuffer, distBufCnt, distBufIdx, 3, dist);
    distAvg = getAvg(distBuffer, distBufCnt); 
   // --- 2) 데이터 가공 ---
   if (!isnan(prev_CO_avg3s)) {
     dCOdt = (CO_avg3s - prev_CO_avg3s) / dt;
   }
   prev_CO_avg3s = CO_avg3s;    
   vehicleFlag = (!isnan(distAvg) && distAvg <= VEHICLE_THRESHOLD);
   // --- 3) 제어 로직 ---
   if (hasRunOnce) {
       // 잠금 상태 (작동 안 함)
   } 
   else { 
       // [트리거 조건 확인]
       bool vehicleEntry = (vehicleFlag && !prevVehicleFlag);
       bool isHighSlope = (!isnan(dCOdt) && dCOdt > SLOPE_THR);
       bool isHighPM    = (pm10_val >= PM10_THR);    
       // [수정] TVOC 트리거는 ppb 기준(265)으로 판단
       bool isHighTVOC  = (tvoc_val >= TVOC_THR);      
       bool pollutionTrigger = (vehicleFlag && (isHighSlope || isHighPM || isHighTVOC));
       // 팬 켜기
       if (!isFanRunning) {
         if (vehicleEntry || pollutionTrigger) {
           digitalWrite(FAN_PIN, FAN_ON_LEVEL);
           isFanRunning = true;
           fanTurnOnTime = now;
           Serial.println(F(">> [EVENT] FAN ON"));
           
           if(vehicleEntry) Serial.println(F("   Reason: Vehicle Entry"));
           if(pollutionTrigger) {
              Serial.print(F("   Reason: Pollution Detected ("));
              if(isHighSlope) Serial.print(F("Slope "));
              if(isHighPM)    Serial.print(F("PM "));
              if(isHighTVOC)  Serial.print(F("TVOC "));
              Serial.println(F(")"));
           }
         }
       }    
       // 팬 끄기 판단
       if (isFanRunning) {
         bool timeExpired = (now - fanTurnOnTime > FAN_MIN_RUN_TIME_MS);
         bool shouldStop = false;
         
         if (operationMode == 1) {
           if (timeExpired) shouldStop = true;
         } 
         else if (operationMode == 2) {
           // [수정] MODE 2 유지 조건도 ppb 기준으로 비교
           bool airClean = (CO_raw < CO_HOLD_THR) && (pm10_val < PM10_THR) && (tvoc_val < TVOC_THR);
           if (timeExpired) {
             if (airClean) shouldStop = true;
             else {
                if ((now / 1000) % 5 == 0) Serial.println(F(">> [HOLD] Fan Extending... (Pollution High)"));
             }
           }
         }
         if (shouldStop) {
           digitalWrite(FAN_PIN, FAN_OFF_LEVEL);
           isFanRunning = false;
           hasRunOnce = true; // 잠금 활성화
           Serial.println(F(">> [SYSTEM STOP] Fan Stopped. System LOCKED."));
           Serial.println(F(">> Press RESET button to restart."));
         }
       }
   } 
   prevVehicleFlag = vehicleFlag;
   // --- 4) 상태 모니터링 출력 ---
   Serial.print(F("[MONITOR] "));   
   if (hasRunOnce) Serial.print(F("LOCKED"));
   else Serial.print(operationMode == 1 ? "M1" : "M2"); 
   Serial.print(F(" | CO:"));    Serial.print(CO_raw, 0);
   Serial.print(F(" | Slope:")); Serial.print(dCOdt, 2);
   Serial.print(F(" | PM:"));    Serial.print(pm10_val);
   //  시리얼 모니터 출력 시 ug/m3 단위로 환산하여 표시
   // 1 ppb * (92.14 / 24.45) = 3.7685 ug/m3
   float tvoc_ugm3 = tvoc_val * 3.7685f; 
   Serial.print(F(" | TVOC(ug):")); Serial.print(tvoc_ugm3, 0); //
   Serial.print(F(" | Dist:")); 
   if(isnan(distAvg)) Serial.print(F("Err"));
   else Serial.print(distAvg, 1);
   Serial.print(F("cm | Vehicle:")); 
   Serial.print(vehicleFlag ? "YES" : "NO");
   Serial.print(F(" | FAN:"));
   Serial.println(isFanRunning ? "ON" : "OFF");
 }
}

결과 및 평가

완료 작품의 소개

프로토타입 사진

시제품 모형.jpg

위 모형은 지하주차장의 주차구획 한 칸을 모사한 것이며, 표지된 MQ-7(CO), HC-SR04(초음파), AM1002(PM-10, VOC) 센서들로 통합 회로를 구축하였다. 또한, 차량 혹은 농도 감지기준 중 한 가지 이상 충족 시 주차블록 후단에 위치한 환기팬이 가동되도록 설계하였다. 팬이 흡기한 공기는 바닥 하부 덕트를 통해 모형 밖으로 배출된다.

실험 결과

팬온.jpg
팬오프.jpg


FAN 작동 유무에 따라 오염물질의 농도가 변하였으며, 그중 1분간 FAN ON의 경우 CO 제어가 가능함을 확인할 수 있다. CO 농도는 최대 기준 29초 후 50% 이상이 저감되었고 1분 후에는 약 65%가 저감되었다. PM-10, VOC 또한 FAN 작동 시 농도가 감소함을 확인하였다.

완료 작품의 평가

숨통결과.jpg

포스터

숨통트여조 포스터.png

개발 사업비 내역서

숨통트여조 개발사업비 내역서.jpg

향후 계획