код для автоматической двери

Я хочу, чтобы она работала как обычная автоматическая дверь, чтобы оба датчика работали как отдельная активация. Единственная проблема, которую я заметил, заключается в том, что сервопривод остается активным, ожидая следующего высокого считывания, и вы можете слышать его жужжание, пытаясь сохранить положение.

До изменения кода этого не было. Пока он открыт, если он считывает другой высокий уровень, он запомнит и повторно запустит открытое ожидание закрытия после его закрытия.

Например. Дверь активируется, и пока она открыта, через нее проходят 3 человека, поэтому, когда дверь закроется, она откроется еще 3 раза, и никого там не будет.

Изменить: код изменен

    #include <Servo.h>

  Servo myservo; 

  int pos = 0; //переменная для хранения положения сервопривода

  //количество времени, которое мы даем датчику для калибровки (10-60 секунд согласно техническому описанию)

  int calibrationTime = 30;

  int pirPin = 12; //цифровой контакт, подключенный к выходу PIR
  int pirPos = 13; // подключается к контакту 5V PIR
  int pirPin2 = 6; //цифровой контакт, подключенный к выходу PIR
  int pirPos2 = 10; // подключается к контакту 5V PIR
  const int position_closed = 0;      // положение двери закрыто [град]
  const int position_open = 90;      // положение двери открыто [град]
  const uint32_t delay_step = 15;     // задержка между шагами сервопривода [мс]
  const uint32_t delay_open = 60000;  // как долго дверь остается открытой [мс]

  void setup()

  {
      myservo.attach(4); // подключаем сервопривод к контакту 4
Serial.begin(9600); //начинает последовательную связь
pinMode(pirPin, INPUT);
pinMode(pirPos, OUTPUT);
digitalWrite(pirPos, HIGH);
pinMode(pirPin2, INPUT);
pinMode(pirPos2, OUTPUT);
digitalWrite(pirPos2, HIGH);

//дать датчику время для калибровки
Serial.println("calibrating sensor ");
for(int i = 0; i < calibrationTime; i++)
{
    Serial.print(calibrationTime - i);
    Serial.print("-");
    delay(1000);
}
Serial.println();
Serial.println("done");
  }


  void loop()
  {
static enum { CLOSED, OPENING, OPEN, CLOSING } state;
static int position;          // положение двери
static uint32_t last_change;  // последнее время изменения состояния или позиции
uint32_t now = millis();

// При обнаружении присутствия переключиться в состояние ОТКРЫТИЯ.
if ((digitalRead(pirPin) == HIGH || digitalRead(pirPin2) == HIGH)
        && state != OPENING) {
    state = OPENING;
    last_change = now;
}
else switch (state) {
    case CLOSED:
        break;
    case OPENING:
        if (position == position_open) {
            state = OPEN;
            last_change = now;
        } else if (now - last_change >= delay_step) {
            myservo.write(++position);
            last_change = now;
        }
        break;
    case OPEN:
        if (now - last_change >= delay_open) {
            state = CLOSING;
            last_change = now;
        }
        break;
    case CLOSING:
        if (position == position_closed) {
            state = CLOSED;
            last_change = now;
        } else if (now - last_change >= delay_step) {
            myservo.write(--position);
            last_change = now;
        }
        break;
      }
  }

, 👍1

Обсуждение

Пожалуйста, отредактируйте свой вопрос, указав, какое поведение вы ДЕЙСТВИТЕЛЬНО видите при запуске кода. Например, вы видите последовательный вывод, ожидаемый во время настройки («калибровка датчика» и «ДАТЧИК АКТИВЕН»)?, @KennetRunner

Вы пытаетесь перейти от 0 до 180, задержать, перейти от 180 до 90, задержать, перейти от 90 до 0? (т.е. 3 стадии: - открыть, закрыть наполовину, закрыть полностью, с задержками между ними) ?, @KennetRunner

Помимо того, что сказал @KennetRunner, нам нужно знать ожидаемое поведение: вы хотите, чтобы _любой_ ИК-датчик открыл дверь, или вы хотите, чтобы _оба_ ИК-датчика что-то обнаружили, чтобы открыть дверь? Поскольку вы находитесь во втором состоянии из приведенного выше кода...., @Roberto Lo Giacco


2 ответа


Лучший ответ:

2

KennetRunner уже объяснил, что не так с вашим кодом. Здесь я буду попробуйте дать совет, как правильно реализовать автоматическую дверь.

Первое, что нужно сделать, это избавиться от delay(). Пока ваша программа находится внутри вызов delay(), он не может ничего сделать, кроме как ждать, пока задержка не закончится. Это допустимо, если программа должна выполнить только одну единственную задачу, как мигание светодиода. Как только у вас есть две задачи, delay() становится неприятно, потому что он блокирует все задачи.

Пример: после открытия дверцы в течение одной минуты ваша программа закрыл бы его, независимо от того, есть ли кто-то на пути. Это не мог видеть, как человек пришел, потому что он был занят задержкой закрытия двери.

Стандартный способ избавиться от delay() — никогда не ждать внутри цикл(). Вместо этого, если есть что сделать прямо сейчас, сделайте это; если не, только не делай, но и не жди. Псевдокод:

void loop()
{
    if (it_is_time_to_do_foo()) {
        do_foo();
    }
    if (it_is_time_to_do_bar()) {
        do_bar();
    }
    /// и т. д...
}

Вы можете использовать функцию millis(), чтобы узнать, когда время истекло. прийти, чтобы выполнить какое-то задание, как описано в разделе Мигание без задержки Учебник по ардуино. Это действительно второй учебник для каждого пользователя Arduino. следует читать сразу после того, как вы научитесь мигать светодиодом.

Затем вам нужно научиться реализовывать конечный автомат. я порекомендуйте руководство по конечному автомату Маженко, постоянный участник этого сайта.

Ваша проблема может быть естественным образом отображена на машине с четырьмя состояния: ЗАКРЫТО, ОТКРЫТИЕ, ОТКРЫТО и ЗАКРЫТИЕ. потом пишешь правила о том, что делать в каждом состоянии и когда переходить в другое состояние. Например:

  • когда дверь ЗАКРЫТА
    • если датчики обнаруживают кого-то, переход в состояние ОТКРЫТИЯ
  • когда дверь ОТКРЫВАЕТСЯ
    • если пришло время переместить сервопривод на один шаг, сделайте это
    • если мы уже прошли весь путь до открытой позиции, мы можем сказать, что дверь ОТКРЫТА сейчас
  • когда дверь ОТКРЫТА
    • если пора закрыть, перейти в состояние ЗАКРЫТИЕ
    • если датчики обнаруживают кого-то, сбросить таймер <<<< обратите внимание!
  • когда дверь ЗАКРЫВАЕТСЯ
    • если датчики обнаруживают кого-то, переход в состояние ОТКРЫТИЯ
    • если пришло время переместить сервопривод на один шаг, сделайте это
    • если мы уже прошли весь путь до закрытой позиции, мы говорим теперь дверь ЗАКРЫТА

Несколько вещей, на которые следует обратить внимание:

  • Обратите внимание на строку, отмеченную символом <<<<: это предотвратит вышеупомянутое проблема закрытия двери в кого-то.
  • Первое правило для состояния ЗАКРЫТИЕ следует из аналогичного обоснования.
  • Я придумал некоторые из этих правил, так как задача вашей программы не полностью указано в вашем вопросе. Не стесняйтесь изменять эти правила на ваш вкус.

А вот и предлагаемая мной реализация. Внимание: не проверено. Как обычный для FSM, он основан на большой инструкции switch/case, идущей через все возможные состояния. Однако, чтобы не повторять правила, я правило «если кто-то есть, начните открывать» из переключатель. Еще я закинул «если есть кто, сбросьте таймер» в то же правило.

const int position_closed = 0;      // положение двери закрыто [град]
const int position_open = 180;      // положение двери открыто [град]
const uint32_t delay_step = 15;     // задержка между шагами сервопривода [мс]
const uint32_t delay_open = 60000;  // как долго дверь остается открытой [мс]

void loop()
{
    static enum { CLOSED, OPENING, OPEN, CLOSING } state;
    static int position;          // положение двери
    static uint32_t last_change;  // последнее время изменения состояния или позиции
    uint32_t now = millis();

    // При обнаружении присутствия переключиться в состояние ОТКРЫТИЯ.
    if ((digitalRead(pirPin) == HIGH || digitalRead(pirPin2) == HIGH)
            && state != OPENING) {
        state = OPENING;
        last_change = now;
    }
    else switch (state) {
        case CLOSED:
            break;
        case OPENING:
            if (position == position_open) {
                state = OPEN;
                last_change = now;
            } else if (now - last_change >= delay_step) {
                myservo.write(++position);
                last_change = now;
            }
            break;
        case OPEN:
            if (now - last_change >= delay_open) {
                state = CLOSING;
                last_change = now;
            }
            break;
        case CLOSING:
            if (position == position_closed) {
                state = CLOSED;
                last_change = now;
            } else if (now - last_change >= delay_step) {
                myservo.write(--position);
                last_change = now;
            }
            break;
    }
}
,

спасибо за код, я включил его из-за того, что у меня была единственная проблема, которую я видел, это то, что сервопривод остается активным, ожидая другого высокого считывания, и вы можете слышать его жужжание, пытающееся сохранить положение, и пока он открыт, если он читает другое высокое, он будет запомнить и перезапустить open ждать закрытия после его закрытия, @ANDREW

@ANDREW: Я не совсем понял твой последний комментарий. «_Сервопривод остается активным [...], пытаясь сохранить положение_»: это нормально, и это именно то, что должен делать сервопривод, когда вы не просите его двигаться в другом месте. «_он запомнит и перезапустит open wait close_»: я не смог воспроизвести описанное вами поведение., @Edgar Bonet

Вы можете слышать постоянный жужжащий шум, который он создает при беге. последний код, который я использовал, издавал шум только при движении, а не при ожидании следующего максимума. Я думал добавить отсоединение в конце и присоединение в начале, это может помочь., @ANDREW

Вы ожидаете, что сервопривод будет шумным, когда он движется или когда в статическом положении он борется с некоторой нагрузкой. Если он статичен и не загружен, он не должен сильно шуметь., @Edgar Bonet


1

Ваш код зацикливается 180 раз, КАЖДЫЙ РАЗ это:

  • Устанавливает сервопривод на 180°
  • Задержки на 2 секунды
  • Пошагово поворачивает сервопривод от 180 до 90°
  • Устанавливает сервопривод на 90°.
  • Задержки на 60 секунд
  • Пошагово поворачивает сервопривод от 90 до 180°

На создание кода уйдет более 3 часов.

Я думаю, вам лучше вынести код, который перемещает сервопривод, в его собственную функцию: что-то вроде:

void moveServo(servoStart, servoEnd, servoStep, servoInterval)
{
  for(int servoPos = servoStart; servoStart < servoEnd; servoStep)
  {
     myservo.write(servoPos);
     delay(servoInterval);
  }
}

затем просто вызовите его из основного цикла, например:

void loop()
{

    if((digitalRead(pirPin) == HIGH) && (digitalRead(pirPin2) == HIGH))
    {
      moveServo(0, 180, 1, 15);
      delay(2000);
      moveServo(180, 90, -1, 15);
      delay(60000);
      moveServo(90, 0, -1, 15);
    }
}
,