Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Manufacturing Technology >> Производственный процесс

Автоматические раздвижные гаражные ворота на Arduino Nano

Компоненты и материалы

Компонент Количество Примечание
Arduino Nano R3 1
Плата для прототипирования 1 Под Nano и OLED
OLED-дисплей 0.96" 128x64 (SPI) 1 Статус, таймеры, датчики
Модуль реле 4-канальный 1 Используются 3 реле
Блок питания 5В (от 220В) 1 Миниатюрный, для логики
Тактильная кнопка 12 мм 1 Кнопка сброса аварии
Двигатель 220В, концевики, фотоэлемент, радиоприемник пульта - От оригинального механизма

Предыстория

Пульт от раздвижных ворот перестал работать. Причина — сгоревшая ящерица на плате управления (короткое замыкание на 220В). Плату восстановить было нельзя — решил собрать свою на Arduino.

⚠ ПРЕДУПРЕЖДЕНИЕ: Проект работает с 220В (или 110В). Опасно для жизни! Только если вы квалифицированно работаете с высоким напряжением. Обязательно протестируйте на макете/симуляторе перед подключением к реальному двигателю.

Архитектура системы

Основной блок управления (Arduino Nano + OLED + реле) управляет:

OLED-дисплей (SPI, пины 11,13,9,10,8) находится внутри корпуса — для настройки и диагностики без ноутбука. Показывает: фазу работы, таймеры, состояния датчиков, команду «Открыть».

Распиновка (GPIO Arduino Nano)

Пин Назначение Компонент
D2FOTOCELULAФотоэлемент (INPUT_PULLUP)
D3OBERTКонцевик «Открыто» (INPUT_PULLUP)
D4OBRIRКоманда «Открыть» от радиоприемника (INPUT_PULLUP)
D5BOMBETAРеле 1: Сигнальная лампа (OUTPUT)
D6MOTOR_OBRIRРеле 2: Двигатель открытия (OUTPUT)
D7TANCATКонцевик «Закрыто» (INPUT_PULLUP)
D12MOTOR_TANCARРеле 3: Двигатель закрытия (OUTPUT)
D11,13,9,10,8OLED SPIMOSI, SCK, DC, CS, RESET

Алгоритм работы (автомат состояний)

Код построен на метках (goto) и четких фазах:

  1. BUCLE_PRINCIPAL — ожидание команды «Открыть», обновление OLED каждые 2 сек. При аварии (AVARIA) лампа мигает 60 сек.
  2. SEQUENCIA_OBERTURA — открытие. Макс. время 28 сек. Лампа медленно мигает (1 сек). Если пришел конец хода «Открыто» -> PORTA_OBERTA. Если повторная команда «Открыть» во время движения -> стоп и переход к закрытию.
  3. PORTA_OBERTA — дверь открыта, ожидание 30 сек перед автозакрытием. Лампа: короткая вспышка каждые 2 сек. Команда «Открыть» -> сразу закрытие.
  4. SEQUENCIA_TANCAMENT — закрытие. Макс. 28 сек. Лампа быстро мигает (300 мс). Фотоэлемент -> стоп и повторное открытие. Концевик «Закрыто» -> BUCLE_PRINCIPAL. Команда «Открыть» -> стоп и открытие.

Код (C/C++ для Arduino Nano)

Две версии: с английскими и каталонскими комментариями. Ниже — английская версия с пояснениями.

/*********************************************************************
 * Автоматизм раздвижных ворот v1.0
 *********************************************************************/
/* Словарь (каталонский -> английский):
 * Bucle principal = Main loop
 * Sequencia Obertura = Opening sequence
 * Sequencia Tancament = Closing sequence
 * Obrir = Open; Obert = Opened
 * Tancar = Close; Tancat = Closed
 * Bombeta = Light bulb; Llum = light
 * Porta = Gate/door; Avaria = Failure
 * Temps = Time; Recorregut = door travel
 */

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// Пины автоматики
int FOTOCELULA = 2;
int BOMBETA = 5;       // Relay 1 - лампа
int TANCAT = 7;        // Концевик "Закрыто"
int OBERT = 3;         // Концевик "Открыто"
int MOTOR_OBRIR = 6;   // Relay 2 - мотор открытия
int MOTOR_TANCAR = 12; // Relay 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;
bool LLUM = false;   // состояние лампы
bool AVARIA = false; // превышено время хода без концевика

void setup() {
  // Выключить моторы и лампу (реле активны по LOW)
  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);

  Serial.begin(9600);
  display.begin(SSD1306_SWITCHCAPVCC);
  display.display(); delay(1000); // лого Adafruit
  display.clearDisplay(); display.display();
  display.setTextSize(1); display.setTextColor(WHITE);
}

void loop() {
  BUCLE_PRINCIPAL:
  delay(2000);
  previousMillis = millis();
  while (digitalRead(OBRIR) == HIGH) { // ждем команду "Открыть" (LOW = нажато)
    updateDisplay("BUCLE PRINCIPAL ...");
    if (AVARIA && (millis() - previousMillis < 60000)) digitalWrite(BOMBETA, LOW);
    else digitalWrite(BOMBETA, HIGH);
  }
  goto SEQUENCIA_OBERTURA;

  SEQUENCIA_OBERTURA:
  interval = 28000; // макс. время открытия
  previousMillis = millis();
  digitalWrite(MOTOR_OBRIR, LOW); // ВКЛ мотор открытия
  digitalWrite(BOMBETA, LOW);     // ВКЛ лампу
  delay(1000);

  A:
  if (digitalRead(OBERT) == HIGH) goto PORTA_OBERTA; // дошел до концевика "Открыто"
  if (digitalRead(OBRIR) == LOW) { // повторная команда "Открыть" -> стоп и ждем закрытия
    updateDisplay("Esperant ordre per tancar");
    digitalWrite(MOTOR_OBRIR, HIGH); digitalWrite(BOMBETA, HIGH);
    delay(1000);
    while (digitalRead(OBRIR) == HIGH) {}; // ждем отпускания кнопки
    goto SEQUENCIA_TANCAMENT;
  }
  updateTimingAndDisplay("Obrint porta");
  if ((millis() - previousMillis) >= interval) { digitalWrite(MOTOR_OBRIR, HIGH); AVARIA = true; }
  flashLight(1000); // медленное мигание
  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) { // команда "Открыть" при закрытии
    updateDisplay("Esperant ordre per obrir");
    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;
  }
  updateTimingAndDisplay("Tancant porta");
  if ((millis() - previousMillis) >= interval) { digitalWrite(MOTOR_TANCAR, HIGH); AVARIA = true; goto BUCLE_PRINCIPAL; }
  flashLight(300); // быстрое мигание
  goto B;

  PORTA_OBERTA:
  interval = 30000; // ожидание 30 сек перед закрытием
  previousMillis = millis();
  digitalWrite(MOTOR_OBRIR, HIGH); delay(500);

  C:
  if (digitalRead(OBRIR) == LOW) goto SEQUENCIA_TANCAMENT; // команда "Открыть" -> закрыть
  updateTimingAndDisplay("Porta Oberta completament");
  if ((millis() - previousMillis) >= interval) goto SEQUENCIA_TANCAMENT;
  // Специальное мигание: 100 мс вкл / 2000 мс выкл
  if (!LLUM) DURADA = 100; else DURADA = 2000;
  if ((millis() - TEMPS_LLUM) >= DURADA) { LLUM = !LLUM; digitalWrite(BOMBETA, LLUM); TEMPS_LLUM = millis(); }
  goto C;
}

// Вспомогательные функции (вынесены для чистоты)
void updateDisplay(const char* phase) {
  currentMillis = millis();
  TEMPS_DISPLAY = (currentMillis - previousMillis) / 1000;
  display.clearDisplay();
  display.setTextSize(1); display.setCursor(0, 0); display.println(phase);
  display.setTextSize(2); display.setCursor(90, 0); display.println(TEMPS_DISPLAY);
  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();
}

void updateTimingAndDisplay(const char* phase) {
  currentMillis = millis();
  TEMPS_DISPLAY = (currentMillis - previousMillis) / 1000;
  updateDisplay(phase);
}

void flashLight(unsigned long period) {
  if ((millis() - TEMPS_LLUM) >= period) {
    LLUM = !LLUM; digitalWrite(BOMBETA, LLUM); TEMPS_LLUM = millis();
  }
}

Схемы и документация

Важные нюансы

Производственный процесс

  1. Компактный breakout для DHT температуры и влажности на Raspberry Pi — ATtiny85 и DHT22/DHT11
  2. Лучшие аксессуары для раскрытия полного потенциала Raspberry Pi
  3. SIGHT: умные очки для слабовидящих на Raspberry Pi
  4. Создание подводного дрона на Raspberry Pi: проектирование, разработка и сборка
  5. Прецизионная автоматизация скользящего головного блока для швейцарского и производственного токарения
  6. Автоматизация как драйвер роста: как малому и среднему бизнесу обеспечить светлое будущее
  7. Искреннее прощание: мой 40-летний путь в издательстве
  8. Как выбрать высококачественные амортизаторы для вашего автомобиля
  9. Ручная ленточная пила: долговечное, портативное решение для резки любого проекта
  10. Что такое автоматический силовой пресс? — Ультимативный станок для формовки листового металла