산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Manufacturing Technology >> 제조공정

아두이노 나노로 구동하는 자동 슬라이딩 차고문 제어기

부품 및 소모품

아두이노 나노 R3 × 1
브레드보드 (나노 및 OLED 장착용) × 1
Adafruit OLED 디스플레이 (0.96", 128x64, SPI) × 1
릴레이 모듈 4채널 (3개 사용) × 1
브레드보드 파워 서플라이 5V/3.3V (220V→5V) × 1
푸시버튼 스위치 12mm (리셋/알람 해제용) × 1

프로젝트 배경

기존 차고문 리모컨이 작동하지 않아 모터 커버를 열어보니, 도마뱀이 220V 단자 사이에서 감전돼 제어 보드가 완전히 타버린 것을 발견했습니다. 이를 계기로 아두이노 기반으로 제어 유닛을 완전히 재설계했습니다.

아두이노 나노로 구동하는 자동 슬라이딩 차고문 제어기
도마뱀 감전으로 탄 원본 제어 보드

⚠️ 안전 경고

이 프로젝트는 220V(또는 110V) 고전압을 직접 다룹니다. 숙련되지 않은 경우 위험할 수 있습니다. 반드시 시뮬레이션 후 실제 모터 회로에 연결하세요.

시스템 구성

아두이노 나노 기반 메인 제어 유닛이 다음을 담당합니다:

핀아웃 요약

핀 기능 비고
D2 포토셀 (장애물 감지, INPUT_PULLUP) 활성 HIGH
D3 닫힘 리미트 스위치 (INPUT_PULLUP) 닫힘 시 HIGH
D4 무선 리시버 "열림" 명령 (INPUT_PULLUP) 명령 시 LOW
D5 릴레이 1: 신호등 (BOMBETA) LOW = ON
D6 릴레이 2: 모터 열림 (MOTOR_OBRIR) LOW = ON
D7 열림 리미트 스위치 (INPUT_PULLUP) 열림 시 HIGH
D8 OLED RESET
D9 OLED DC
D10 OLED CS
D11 OLED MOSI
D12 릴레이 3: 모터 닫힘 (MOTOR_TANCAR) LOW = ON
D13 OLED CLK

OLED 디스플레이 표시 정보

플로우차트 기반 로직

세 개의 주요 상태 시퀀스로 구성:

  1. BUCLE_PRINCIPAL (메인 루프): "열림" 명령 대기, 센서 상태 표시, 고장 시 신호등 점멸
  2. SEQUENCIA_OBERTURA (열림 시퀀스): 모터 정회전, 열림 리미트 감지 시 PORTA_OBERTA로, 최대 시간 초과 시 고장 플래그 설정, 신호등 느린 점멸
  3. PORTA_OBERTA (완전 열림 대기): 30초 대기 후 자동 닫힘 시퀀스 진입, 열림 명령 재수신 시 즉시 닫힘, 신호등 특수 패턴 점멸
  4. SEQUENCIA_TANCAMENT (닫힘 시퀀스): 모터 역회전, 닫힘 리미트 감지 시 메인 루프로, 포토셀 감지 시 즉시 열림 시퀀스로 복귀, 열림 명령 수신 시 열림 시퀀스로, 최대 시간 초과 시 고장, 신호등 빠른 점멸

코드 (영문 주석 버전)

/*********************************************************************
 * 자동 슬라이딩 도어 제어기 - English Version
 *********************************************************************/
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// 제어 핀 정의
int FOTOCELULA = 2;      // 포토셀
int BOMBETA = 5;         // 릴레이 1: 신호등
int TANCAT = 7;          // 닫힘 리미트
int OBERT = 3;           // 열림 리미트
int MOTOR_OBRIR = 6;     // 릴레이 2: 모터 열림
int MOTOR_TANCAR = 12;   // 릴레이 3: 모터 닫힘
int OBRIR = 4;           // 무선 리시버 열림 명령

// OLED 핀 (SPI)
#define OLED_MOSI 11
#define OLED_CLK 13
#define OLED_DC 9
#define OLED_CS 10
#define OLED_RESET 8

Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);

// 타이밍 변수
unsigned long TEMPS_PORTA_OBERTA = 20000;   // 열림 유지 시간
unsigned long TEMPS_MAXIM_RECORREGUT = 15000; // 최대 이동 시간
int interval = 1000;
int TEMPS_DISPLAY = 1;
int DURADA = 300;
unsigned long previousMillis = 0;
unsigned long currentMillis = 0;
unsigned long TEMPS_LLUM = 1000;
boolean LLUM = false;
boolean AVARIA = false;

void setup() {
  // 모터/램프 OFF (릴레이 HIGH = OFF)
  digitalWrite(MOTOR_OBRIR, HIGH);
  digitalWrite(MOTOR_TANCAR, HIGH);
  digitalWrite(BOMBETA, HIGH);
  
  // 핀 모드 설정
  pinMode(FOTOCELULA, INPUT_PULLUP);
  pinMode(TANCAT, INPUT_PULLUP);
  pinMode(OBERT, INPUT_PULLUP);
  pinMode(OBRIR, INPUT_PULLUP);
  pinMode(BOMBETA, OUTPUT);
  pinMode(MOTOR_OBRIR, OUTPUT);
  pinMode(MOTOR_TANCAR, OUTPUT);
  
  // OLED 초기화
  Serial.begin(9600);
  display.begin(SSD1306_SWITCHCAPVCC);
  display.display(); delay(1000);
  display.clearDisplay(); display.display();
  display.setTextSize(1); display.setTextColor(WHITE);
}

void loop() {
  BUCLE_PRINCIPAL:
  delay(2000);
  previousMillis = millis();
  
  // 열림 명령 대기 중 센서 상태 표시
  while (digitalRead(OBRIR) == HIGH) {
    updateDisplay("BUCLE PRINCIPAL");
    if (AVARIA && (millis() - previousMillis < 60000)) digitalWrite(BOMBETA, LOW);
    else digitalWrite(BOMBETA, HIGH);
  }
  goto SEQUENCIA_OBERTURA;

  SEQUENCIA_OBERTURA:
  interval = 28000;  // 최대 열림 시간 (28초)
  previousMillis = millis();
  digitalWrite(MOTOR_OBRIR, LOW);  // 모터 열림 ON
  digitalWrite(BOMBETA, LOW);      // 신호등 ON
  delay(1000);
  
  A:
  if (digitalRead(OBERT) == HIGH) goto PORTA_OBERTA;  // 열림 완료
  
  // 열림 명령 재수신 시 즉시 닫힘 시퀀스로
  if (digitalRead(OBRIR) == LOW) {
    digitalWrite(MOTOR_OBRIR, HIGH); digitalWrite(BOMBETA, HIGH);
    delay(1000);
    while (digitalRead(OBRIR) == HIGH) {};  // 명령 해제 대기
    goto SEQUENCIA_TANCAMENT;
  }
  
  // 시간/디스플레이 업데이트
  currentMillis = millis();
  TEMPS_DISPLAY = (currentMillis - previousMillis) / 1000;
  updateDisplay("Obrint porta", TEMPS_DISPLAY);
  
  // 타임아웃 → 고장
  if ((currentMillis - previousMillis) >= interval) {
    digitalWrite(MOTOR_OBRIR, HIGH); AVARIA = true;
  }
  
  // 신호등 느린 점멸 (1초 주기)
  if ((currentMillis - TEMPS_LLUM) >= 1000) {
    LLUM = !LLUM; digitalWrite(BOMBETA, LLUM); TEMPS_LLUM = currentMillis;
  }
  goto A;

  SEQUENCIA_TANCAMENT:
  interval = 28000;  // 최대 닫힘 시간
  previousMillis = millis();
  digitalWrite(MOTOR_TANCAR, LOW); digitalWrite(BOMBETA, LOW);
  if (digitalRead(TANCAT) == LOW) delay(1000);
  
  B:
  if (digitalRead(TANCAT) == HIGH) {  // 닫힘 완료
    digitalWrite(MOTOR_TANCAR, HIGH); digitalWrite(BOMBETA, HIGH);
    goto BUCLE_PRINCIPAL;
  }
  
  // 열림 명령 수신 → 열림 시퀀스
  if (digitalRead(OBRIR) == LOW) {
    digitalWrite(MOTOR_TANCAR, HIGH); digitalWrite(BOMBETA, HIGH);
    delay(1000);
    while (digitalRead(OBRIR) == HIGH) {};
    goto SEQUENCIA_OBERTURA;
  }
  
  // 포토셀 감지 → 즉시 열림 시퀀스 (안전)
  if (digitalRead(FOTOCELULA) == HIGH) {
    digitalWrite(MOTOR_TANCAR, HIGH); delay(1000);
    goto SEQUENCIA_OBERTURA;
  }
  
  // 시간/디스플레이 업데이트
  currentMillis = millis();
  TEMPS_DISPLAY = (currentMillis - previousMillis) / 1000;
  updateDisplay("Tancant porta", TEMPS_DISPLAY);
  
  // 타임아웃 → 고장
  if ((currentMillis - previousMillis) >= interval) {
    digitalWrite(MOTOR_TANCAR, HIGH); AVARIA = true; goto BUCLE_PRINCIPAL;
  }
  
  // 신호등 빠른 점멸 (300ms 주기)
  if ((currentMillis - TEMPS_LLUM) >= 300) {
    LLUM = !LLUM; digitalWrite(BOMBETA, LLUM); TEMPS_LLUM = currentMillis;
  }
  goto B;

  PORTA_OBERTA:
  interval = 30000;  // 열림 유지 30초
  previousMillis = millis();
  digitalWrite(MOTOR_OBRIR, HIGH); delay(500);
  
  C:
  if (digitalRead(OBRIR) == LOW) goto SEQUENCIA_TANCAMENT;  // 즉시 닫힘
  
  // 시간/디스플레이 업데이트
  currentMillis = millis();
  TEMPS_DISPLAY = (currentMillis - previousMillis) / 1000;
  updateDisplay("Porta Oberta completament", TEMPS_DISPLAY);
  
  // 30초 경과 → 자동 닫힘
  if ((currentMillis - previousMillis) >= interval) goto SEQUENCIA_TANCAMENT;
  
  // 특수 점멸 패턴 (짧은 ON 100ms, 긴 OFF 2초)
  if (!LLUM) DURADA = 100; else DURADA = 2000;
  if ((currentMillis - TEMPS_LLUM) >= DURADA) {
    LLUM = !LLUM; digitalWrite(BOMBETA, LLUM); TEMPS_LLUM = currentMillis;
  }
  goto C;
}

// 디스플레이 업데이트 헬퍼 함수 (실제 코드에선 인라인으로 전개됨)
void updateDisplay(const char* status, int seconds) {
  display.clearDisplay();
  display.setTextSize(1); display.setCursor(0, 0); display.println(status);
  display.setTextSize(2); display.setCursor(90, 0); display.println(seconds);
  display.setTextSize(1); display.setCursor(0, 20);
  display.print("Fotocel.lula: "); display.println(digitalRead(FOTOCELULA));
  display.print("Sensor TANCAT: "); display.println(digitalRead(TANCAT));
  display.print("Sensor OBERT: "); display.println(digitalRead(OBERT));
  display.print("Ordre OBRIR: "); display.println(!digitalRead(OBRIR));
  display.display();
}

회로도 및 참고자료

개선 포인트: 원본 상용 제어기에 없던 안전 기능(포토셀 기반 즉시 역주행, 이동 타임아웃 감지, 상태 표시)을 추가했습니다. OLED는 내부 커버 안에 장착돼 평소엔 보이지 않으며, 설정/튜닝 시에만 활용됩니다.

제조공정

  1. 라즈베리 파이용 콤팩트 DHT 온습도 브레이크아웃: ATtiny85와 DHT22/DHT11
  2. 라즈베리 파이의 잠재력을 100% 끌어내는 필수 액세서리
  3. SIGHT: 시각장애인을 돕는 스마트 안경
  4. 라즈베리 파이로 수중 드론 만들기: 설계, 개발, 조립 전 과정
  5. 트라우브 TNL18 P: 5축 원세팅 생산, 의료·항공 부품 정밀 가공
  6. 자동화가 중소기업의 밝은 미래를 이끄는 성장 동력
  7. 진심 어린 작별: 출판계 40년 여정을 마치며
  8. 차량용 최고 품질 쇼크업소버 선택 방법
  9. 수동 밴드쏘: 내구성·이동성 겸비한 만능 절단 솔루션
  10. 자동 파워 프레스란? - 궁극의 시트 메탈 성형 기계